-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy path02-Rplot.Rmd
4198 lines (3162 loc) · 150 KB
/
02-Rplot.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# R plots {#Rplots}
## qplot绘制图形 (王绪宁) {#plot}
ggplot2中两大精髓的函数分别为 `qplot` (快速作图quick plot),类似于R中基本的`plot`和`ggplot` (更符合ggplot2图层式绘图理念,一层层添加修改)。
测试数据是`ggplot2`包中自带的`diamond`数据,每一行为一种钻石,每一列为钻石不同的属性,如`carat` (克拉), `cut` (切工), `color` (色泽), `clarity` (透明度)等。
```{r}
library(ggplot2)
set.seed(23)
dat <- diamonds[sample(nrow(diamonds), 1000),]
head(dat)
```
数据读进来后,怎么绘制呢?
1. 绘制散点图,横轴是克拉数,纵轴是价格 (正相关)
```{r}
qplot(carat,price,data=dat)
```
绘制散点图,对x,y值取log
```{r}
qplot(log(carat),log(price),data=dat)
```
颜色、大小、性状和其他属性的设置
```{r}
qplot(carat,price,data=dat,colour=color)
```
```{r}
qplot(carat,price,data=dat,shape=cut)#以cut为分类依据设置不同的形状
```
**几何对象**
`qplot()`函数配合不同的几何对象便可绘制出不同的图形:
* 点图 `geom="point"`
* 平滑曲线 `geom="smooth"`
* 箱线图 `geom="boxplot"`
* 任意方向的路径性`geom="path"`
* 线条图 `geom="line"` (从左到右连接)
* 对于连续变量,直方图`geom="histogram"`
* 频率多边图 `geom="freqpoly"`
* 绘制密度曲线 `geom="density"`
* 如果只有x参数传递给qplot(),那么默认是直方图
* 对于离散变量,geom=“bar”绘制条形图
```{r}
#向图形中加入平滑曲线(#从本张图片可以逐渐体会ggplot绘图的强大,
#后期应用ggplot()函数后,可以更加自由的绘制各种组合图形)
qplot(carat,price,data=dat,geom=c("point","smooth"))#添加了一条拟合曲线
#拟合曲线默认的方法为method="loss",程序会根据数据点的多少自动选取,曲线周围的灰色部分为标准误,可以用se=FALSE曲线
```
**绘制其他常见图形**
1. 箱线图
```{r}
qplot(color,price/carat,data=dat,geom="boxplot")
```
2. 绕动图 (抖动图)
```{r}
qplot(color,price/carat,data=diamonds,geom="jitter")
```
3. 直方图
```{r}
qplot(carat,data=dat,geom="histogram")
```
4. 密度曲线图
```{r}
qplot(carat,data=dat,geom="density")
```
5. 条形图
```{r}
qplot(color,data=dat,geom="bar")
```
6. 时间序列线条图,采用另一个数据集economics
```{r}
qplot(date,unemploy/pop,data=economics,geom="line")
```
7. 分面绘图`facet`=分类变量
```{r, fig.height=10}
qplot(carat,data=dat,facets=color ~ .,geom="histogram",binwidth=0.1,xlim=c(0,3))
```
以上为ggplot2包中常见图形的快速绘制 (`quickplot`)即`qplot`函数的应用。
`qplot`函数还有很多其他的参数, 对`xlim`,`ylim`设置`x`,`y`轴的区间例如`xlim=c(0,20)`; 对轴取`log`值,`log="x"`,对x轴取对数,`log="xy"`表示对x轴和y轴取对数;main:图形的主题main="qplot title"; #xlab,ylab:设置轴标签文字
```{r}
qplot(carat,price,data=dat,
xlab="Price($)",ylab="Weight(carat)",
main="Price-Weight relationship")
```
## 热图绘制 {#ggplot2_heatmap}
热图是做分析时常用的展示方式,简单、直观、清晰。可以用来显示基因在不同样品中表达的高低、表观修饰水平的高低、样品之间的相关性等。任何一个数值矩阵都可以通过合适的方式用热图展示。
本篇使用R的`ggplot2`包实现从原始数据读入到热图输出的过程,并在教程结束后提供一份封装好的命令行绘图工具,只需要提供矩阵,即可一键绘图。
上一篇讲述了Rstudio的使用作为R写作和编译环境的入门,后面的命令都可以拷贝到Rstudio中运行,或写成一个R脚本,使用`Rscript heatmap.r`运行。我们还提供了Bash的封装,在不修改R脚本的情况下,改变参数绘制出不同的图形。
### 生成测试数据 {#ggplot2_heatmap_data}
绘图首先需要数据。通过生成几组向量,转换为矩阵,得到想要的数据。
```{r}
data <- c(1:6,6:1,6:1,1:6, (6:1)/10,(1:6)/10,(1:6)/10,(6:1)/10,1:6,6:1,6:1,1:6,
6:1,1:6,1:6,6:1)
data
```
**注意**:运算符的优先级。
```{r}
1:3+4
```
```{r}
(1:3)+4
```
```{r}
1:(3+4)
```
Vector转为矩阵 (matrix),再转为数据框 (data.frame)。
```{r}
# ncol: 指定列数
# byrow: 先按行填充数据
# ?matrix 可查看函数的使用方法
# as.data.frame的as系列是转换用的
data <- as.data.frame(matrix(data, ncol=12, byrow=T))
data
```
```{r}
# 增加列的名字
colnames(data) <- c("Zygote","2_cell","4_cell","8_cell","Morula","ICM","ESC",
"4 week PGC","7 week PGC","10 week PGC","17 week PGC", "OOcyte")
# 增加行的名字
# 注意paste和paste0的使用
rownames(data) <- paste("Gene", 1:8, sep="_")
# 只显示前6行和前4列
head(data)[,1:4]
```
虽然方法比较繁琐,但一个数值矩阵已经获得了。
还有另外2种获取数值矩阵的方式。
* 读入字符串
```{r}
# 使用字符串的好处是不需要额外提供文件
# 简单测试时可使用,写起来不繁琐,又方便重复
# 尤其适用于在线提问时作为测试案例
txt <- "ID;Zygote;2_cell;4_cell;8_cell
+ Gene_1;1;2;3;4
+ Gene_2;6;5;4;5
+ Gene_3;0.6;0.5;0.4;0.4"
# 习惯设置quote为空,避免部分基因名字或注释中存在引号,导致读入文件错误。
# 具体错误可查看 http://blog.genesino.com/collections/R_tips/ 中的记录
data2 <- read.table(text=txt,sep=";", header=T, row.names=1, quote="")
head(data2)
```
可以看到列名字中以数字开头的列都加了**X**。一般要尽量避免行或列名字以**数字开头**,会给后续分析带来匹配问题;另外名字中出现的非字母、数字、下划线、点的字符都会被转为**点**,也需要注意,尽量只用字母、下划线和数字。
```{r}
# 读入时,增加一个参数`check.names=F`也可以解决问题。
# 这次数字前没有再加 X 了
data2 <- read.table(text=txt,sep=";", header=T, row.names=1, quote="", check.names = F)
head(data2)
```
* 读入文件
与上一步类似,只是把`txt`代表的文字存到文件中,再利用文件名读取,不再赘述。
```{r}
#data2 <- read.table("filename",sep=";", header=T, row.names=1, quote="")
```
### 转换数据格式 {#ggplot2_heatmap_dataformat}
数据读入后,还需要一步格式转换。在使用ggplot2作图时,有一种长表格模式是最为常用的,尤其是数据不规则时,更应该使用 (这点,我们在讲解箱线图时再说)。
`melt`:把正常矩阵转换为长表格模式的函数。工作原理是把全部的非id列的`数值列`转为1列 (列名默认为`value`);所有字符列转为一列,列名默认为`variable`。
```{r}
# 如果包没有安装,运行下面一句,安装包
#install.packages(c("reshape2","ggplot2"))
library(reshape2)
library(ggplot2)
# 转换前,先增加一列ID列,保存行名字
data$ID <- rownames(data)
# id.vars 列用于指定哪些列为id列;这些列不会被merge,会保留为完整一列。
data_m <- melt(data, id.vars=c("ID"))
head(data_m)
```
### 分解绘图 {#ggplot2_heatmap_split}
数据转换后就可以画图了,分解命令如下:
```{r}
# data_m: 是前面费了九牛二虎之力得到的数据表
# aes: aesthetic的缩写,一般指定整体的X轴、Y轴、颜色、形状、大小等。
# 在最开始读入数据时,一般只指定x和y,其它后续指定
p <- ggplot(data_m, aes(x=variable,y=ID))
# 热图就是一堆方块根据其值赋予不同的颜色,所以这里使用fill=value, 用数值做填充色。
p <- p + geom_tile(aes(fill=value))
# ggplot2为图层绘制,一层层添加,存储在p中,在输出p的内容时才会出图。
p
## 如果你没有使用Rstudio或其它R图形版工具,而是在远程登录的服务器上运行的交互式R,
## 需要输入下面的语句,获得输出图形 (图形存储于R的工作目录下的Rplots.pdf文件中)。
## 如何指定输出,后面会讲到。
#dev.off()
```
热图出来了,但有点不对劲,横轴重叠一起了。一个办法是调整图像的宽度,另一个是旋转横轴标记。
```{r}
# theme: 是处理图美观的一个函数,可以调整横纵轴label的选择、图例的位置等。
# 这里选择X轴标签45度。
# hjust和vjust调整标签的相对位置,
# 具体见下图。
# 简单说,hjust是水平的对齐方式,0为左,1为右,0.5居中,0-1之间可以取任意值。
# vjust是垂直对齐方式,0底对齐,1为顶对齐,0.5居中,0-1之间可以取任意值。
p <- p + theme(axis.text.x=element_text(angle=45,hjust=1, vjust=1))
p
```
```{r}
#knitr::include_graphics("images/hjust_vjust.png")
td <- expand.grid(
hjust=c(0, 0.5, 1),
vjust=c(0, 0.5, 1),
angle=c(0, 45, 90),
text="text"
)
ggplot(td, aes(x=hjust, y=vjust)) +
geom_point() +
geom_text(aes(label=text, angle=angle, hjust=hjust, vjust=vjust)) +
facet_grid(~angle) +
scale_x_continuous(breaks=c(0, 0.5, 1), expand=c(0, 0.2)) +
scale_y_continuous(breaks=c(0, 0.5, 1), expand=c(0, 0.2))
```
<http://stackoverflow.com/questions/7263849/what-do-hjust-and-vjust-do-when-making-a-plot-using-ggplot>
设置想要的颜色。
```{r}
# 连续的数字,指定最小数值代表的颜色和最大数值赋予的颜色
# 注意fill和color的区别,fill是填充,color只针对边缘
p <- p + scale_fill_gradient(low = "white", high = "red")
p
```
调整legend的位置, `legend.position`, 可以接受的值有 `top`, `bottom`, `left`, `right`, 和一个坐标 `c(0.05,0.8)` (左上角,坐标是相对于图的左下角(即**原点**)计算的)
```{r}
p <- p + theme(legend.position="top")
p
```
调整背景和背景格线以及X轴、Y轴的标题。
```{r}
p <- p + xlab("samples") + theme_bw() + theme(panel.grid.major = element_blank()) +
theme(legend.key=element_blank())
p
```
为了使横轴旋转45度,需要把这句话`theme(axis.text.x=element_text(angle=45,hjust=1, vjust=1))`放在`theme_bw()`的后面。
```{r}
p <- p + theme(axis.text.x=element_text(angle=45,hjust=1, vjust=1))
p
```
合并以上命令,就得到了下面这个看似复杂的绘图命令。
```{r}
p <- ggplot(data_m, aes(x=variable,y=ID)) + xlab("samples") + theme_bw() +
theme(panel.grid.major = element_blank()) + theme(legend.key=element_blank()) +
theme(axis.text.x=element_text(angle=45,hjust=1, vjust=1)) +
theme(legend.position="top") + geom_tile(aes(fill=value)) +
scale_fill_gradient(low = "white", high = "red") +
geom_point(aes(color=value), size=6) +
geom_text(aes(label=value))
p
```
也可以只用Point
```{r}
p <- ggplot(data_m, aes(x=variable,y=ID)) + xlab("samples") + theme_bw() +
theme(panel.grid.major = element_blank()) + theme(legend.key=element_blank()) +
theme(axis.text.x=element_text(angle=45,hjust=1, vjust=1)) +
theme(legend.position="top") +
geom_point(aes(color=value), size=6) +
scale_color_gradient(low = "white", high = "red") +
geom_text(aes(label=value))
p
```
### 图形存储 {#ggplot2_heatmapsave}
图形出来了,就得考虑存储了,一般输出为`PDF`格式,方便后期的修改。
```{r}
# 可以跟输出文件不同的后缀,以获得不同的输出格式
# colormode支持srgb (屏幕)和cmyk (打印,部分杂志需要,看上去有点褪色的感觉)格式
ggsave(p, filename="heatmap.pdf", width=10,
height=15, units=c("cm"),colormodel="srgb")
```
点击下载:[pdf](heatmap.pdf)
至此,完成了简单的heatmap的绘图。但实际绘制时,经常会碰到由于数值变化很大,导致颜色过于集中,使得图的可读性下降很多。因此需要对数据进行一些处理,具体的下次再说。
## 热图美化 {#ggplot2_heatmapmeihua}
上面的测试数据,数值的分布比较均一,相差不是太大,但是`Gene_4`和`Gene_5`由于整体的值低于其它的基因,从颜色上看,不仔细看,看不出差别。
```{r}
data <- c(rnorm(5,mean=5), rnorm(5,mean=20), rnorm(5, mean=100), c(600,700,800,900,10000))
data <- matrix(data, ncol=5, byrow=T)
data <- as.data.frame(data)
rownames(data) <- letters[1:4]
colnames(data) <- paste("Grp", 1:5, sep="_")
data
```
```{r}
data$ID <- rownames(data)
data
```
```{r}
data_m <- melt(data, id.vars=c("ID"))
head(data_m)
```
```{r}
p <- ggplot(data_m, aes(x=variable,y=ID)) + xlab("samples") + theme_bw() +
theme(panel.grid.major = element_blank()) + theme(legend.key=element_blank()) +
theme(axis.text.x=element_text(angle=45,hjust=1, vjust=1)) +
theme(legend.position="top") + geom_tile(aes(fill=value)) +
scale_fill_gradient(low = "white", high = "red")
p
#dev.off()
```
图中只有右上角可以看到红色,其他地方就没了颜色的差异。这通常不是我们想要的。为了更好的可视化效果,需要对数据做些预处理,主要有 `对数转换`,`Z-score转换`,`抹去异常值`,`非线性颜色`等方式。
### 对数转换 {#ggplot2_heatmap_log}
假设下面的数据是基因表达数据,4个基因 (a, b, c, d)和5个样品 (Grp_1, Grp_2, Grp_3, Grp_4),矩阵中的值代表基因表达FPKM值。
```{r}
data <- c(rnorm(5,mean=5), rnorm(5,mean=20), rnorm(5, mean=100), c(600,700,800,900,10000))
data <- matrix(data, ncol=5, byrow=T)
data <- as.data.frame(data)
rownames(data) <- letters[1:4]
colnames(data) <- paste("Grp", 1:5, sep="_")
data
```
```{r}
data_log <- log2(data+1)
data_log
```
```{r}
data_log$ID = rownames(data_log)
data_log_m = melt(data_log, id.vars=c("ID"))
p <- ggplot(data_log_m, aes(x=variable,y=ID)) + xlab("samples") + ylab(NULL) +
theme_bw() + theme(panel.grid.major = element_blank()) +
theme(legend.key=element_blank()) + theme(legend.position="top") +
theme(axis.text.x=element_text(angle=45,hjust=1,vjust=1)) +
geom_tile(aes(fill=value)) + scale_fill_gradient(low = "white", high = "red")
p
#ggsave(p, filename="heatmap_log.pdf", width=8, height=12, units=c("cm"),colormodel="srgb")
```
对数转换后的数据,看起来就清晰的多了。而且对数转换后,数据还保留着之前的变化趋势,不只是基因在不同样品之间的表达可比 (同一行的不同列),不同基因在同一样品的值也可比 (同一列的不同行) (不同基因之间比较表达值存在理论上的问题,即便是按照长度标准化之后的FPKM也不代表基因之间是完全可比的)。
### Z-score转换 {#ggplot2_heatmapzscore}
`Z-score`又称为标准分数,是一组数中的每个数减去这一组数的平均值再除以这一组数的标准差,代表的是原始分数距离原始平均值的距离,以标准差为单位。可以对不同分布的各原始分数进行比较,用来反映数据的相对变化趋势,而非绝对变化量。
```{r}
data_ori <- "Grp_1;Grp_2;Grp_3;Grp_4;Grp_5
a;6.6;20.9;100.1;600.0;5.2
b;20.8;99.8;700.0;3.7;19.2
c;100.0;800.0;6.2;21.4;98.6
d;900;3.3;20.3;101.1;10000"
data <- read.table(text=data_ori, header=T, row.names=1, sep=";", quote="")
# 去掉方差为0的行,也就是值全都一致的行
data <- data[apply(data,1,var)!=0,]
data
```
```{r}
# 标准化数据,并转换为data.frame
data_scale <- as.data.frame(t(apply(data,1,scale)))
# 重命名列
colnames(data_scale) <- colnames(data)
data_scale
```
```{r}
data_scale$ID = rownames(data_scale)
data_scale_m = melt(data_scale, id.vars=c("ID"))
data_scale_m$value <- as.numeric(prettyNum(data_scale_m$value, digits=2))
p <- ggplot(data_scale_m, aes(x=variable,y=ID)) + xlab("samples") + ylab(NULL) +
theme_bw() + theme(panel.grid.major = element_blank()) +
theme(legend.key=element_blank()) +
theme(axis.text.x=element_text(angle=45,hjust=1, vjust=1)) +
geom_tile(aes(fill=value)) + scale_fill_gradient(low = "white", high = "red") +
geom_text(aes(label=value))
p
#ggsave(p, filename="heatmap_scale.pdf", width=8, height=12, units=c("cm"),
# colormodel="srgb")
```
`Z-score`转换后,颜色分布也相对均一了,每个基因在不同样品之间的表达的高低一目了然。但是不同基因之间就完全不可比了。
### 抹去异常值 {#ggplot2_heatmapoutlier}
粗暴一点,假设检测饱和度为100,大于100的值都视为100对待。
```{r}
data_ori <- "Grp_1;Grp_2;Grp_3;Grp_4;Grp_5
a;6.6;20.9;100.1;600.0;5.2
b;20.8;99.8;700.0;3.7;19.2
c;100.0;800.0;6.2;21.4;98.6
d;900;3.3;20.3;101.1;10000"
data <- read.table(text=data_ori, header=T, row.names=1, sep=";", quote="")
data[data>100] <- 100
data
```
```{r}
data$ID = rownames(data)
data_m = melt(data, id.vars=c("ID"))
p <- ggplot(data_m, aes(x=variable,y=ID)) + xlab("samples") + ylab(NULL) + theme_bw() +
theme(panel.grid.major = element_blank()) + theme(legend.key=element_blank()) +
theme(axis.text.x=element_text(angle=45,hjust=1, vjust=1)) +
geom_tile(aes(fill=value)) + scale_fill_gradient(low = "white", high = "red") +
geom_text(aes(label=value))
p
#ggsave(p, filename="heatmap_nooutlier.pdf", width=8, height=12, units=c("cm"),
# colormodel="srgb")
```
虽然损失了一部分信息,但整体模式还是出来了。**但是在选择异常值标准时需要根据实际确认**。
### 非线性颜色 {#ggplot2_heatmapnonlinear}
正常来讲,颜色的赋予在最小值到最大值之间是均匀分布的。如果最小值到最大值之间用100个颜色区分,则其中每一个`bin`,不论其大小、有没有值都会赋予一个颜色。非线性颜色则是对数据比较小但密集的地方赋予更多颜色,数据大但分布散的地方赋予更少颜色,这样既能加大区分度,又最小的影响原始数值。通常可以根据数据模式,手动设置颜色区间。为了方便自动化处理,也可选择用**四分位数**的方式设置颜色区间。
```{r}
data_ori <- "Grp_1;Grp_2;Grp_3;Grp_4;Grp_5
a;6.6;20.9;100.1;600.0;5.2
b;20.8;99.8;700.0;3.7;19.2
c;100.0;800.0;6.2;21.4;98.6
d;900;3.3;20.3;101.1;10000"
data <- read.table(text=data_ori, header=T, row.names=1, sep=";", quote="")
data
```
获取数据的最大、最小、第一四分位数、中位数、第三四分位数
```{r}
data$ID = rownames(data)
data_m = melt(data, id.vars=c("ID"))
summary_v <- summary(data_m$value)
summary_v
```
在最小值和第一四分位数之间划出6个区间,第一四分位数和中位数之间划出6个区间,中位数和第三四分位数之间划出5个区间,最后的数划出5个区间
```{r}
break_v <- unique(c(seq(summary_v[1]*0.95,summary_v[2],length=6),
seq(summary_v[2],summary_v[3],length=6),seq(summary_v[3],summary_v[5],length=5),
seq(summary_v[5],summary_v[6]*1.05,length=5)))
break_v
```
按照设定的区间分割数据, 原始数据替换为了其所在的区间的数值
```{r}
data_m$value <- cut(data_m$value, breaks=break_v,labels=break_v[2:length(break_v)])
break_v=unique(data_m$value)
data_m
```
虽然看上去还是数值,但已经不是数字类型了,而是不同的因子了,这样就可以对不同的因子赋予不同的颜色了
```{r}
is.numeric(data_m$value)
```
```{r}
is.factor(data_m$value)
```
```{r}
break_v
```
产生对应数目的颜色
```{r}
gradientC=c('green','yellow','red')
col <- colorRampPalette(gradientC)(length(break_v))
col
```
```{r}
p <- ggplot(data_m, aes(x=variable,y=ID)) + xlab("samples") + ylab(NULL) + theme_bw() +
theme(panel.grid.major = element_blank()) + theme(legend.key=element_blank()) +
theme(axis.text.x=element_text(angle=45,hjust=1, vjust=1)) + geom_tile(aes(fill=value))
# 与上面不同的地方,使用的是scale_fill_manual逐个赋值
p <- p + scale_fill_manual(values=col)
p
#ggsave(p, filename="heatmap_nonlinear.pdf", width=8, height=12, units=c("cm"),colormodel="srgb")
```
### 调整行或列的顺序 {#ggplot2_heatmaporder}
如果想保持图中每一行的顺序与输入的数据框一致,需要设置因子的水平。这也是`ggplot2`中调整图例或横纵轴字符顺序的常用方式。
```{r}
data_rowname <- rownames(data)
data_rowname <- as.vector(rownames(data))
data_rownames <- rev(data_rowname)
data_log_m$ID <- factor(data_log_m$ID, levels=data_rownames, ordered=T)
p <- ggplot(data_log_m, aes(x=variable,y=ID)) + xlab(NULL) + ylab(NULL) + theme_bw() +
theme(panel.grid.major = element_blank()) + theme(legend.key=element_blank()) +
theme(axis.text.x=element_text(angle=45,hjust=1, vjust=1)) +
theme(legend.position="top") + geom_tile(aes(fill=value)) +
scale_fill_gradient(low = "white", high = "red")
p
#ggsave(p, filename="heatmap_log.pdf", width=8, height=12, units=c("cm"),colormodel="srgb")
```
基于ggplot2的heatmap绘制到现在就差不多了,但总是这么画下去也会觉得有点累,有没有办法更简化呢?。
## 热图绘制 - pheatmap {#pheatmap_simple}
绘制热图除了使用`ggplot2`,还可以有其它的包或函数,比如`pheatmap::pheatmap` (pheatmap包中的pheatmap函数)、`gplots::heatmap.2`等。
相比于`ggplot2`作heatmap, `pheatmap`会更为简单一些,一个函数设置不同的参数,可以完成行列聚类、行列注释、`Z-score`计算、颜色自定义等。那我们来看看效果怎样。
```{r}
data_ori <- "Grp_1;Grp_2;Grp_3;Grp_4;Grp_5
a;6.6;20.9;100.1;600.0;5.2
b;20.8;99.8;700.0;3.7;19.2
c;100.0;800.0;6.2;21.4;98.6
d;900;3.3;20.3;101.1;10000"
data <- read.table(text=data_ori, header=T, row.names=1, sep=";", quote="")
```
```{r}
#pheatmap::pheatmap(data, filename="pheatmap_1.pdf")
pheatmap::pheatmap(data)
```
虽然有点丑,但一步就出来了。
在`heatmap美化`篇提到的数据前期处理方式,都可以用于`pheatmap`的画图。此外`Z-score`计算在`pheatmap`中只要一个参数就可以实现。
```{r}
pheatmap::pheatmap(data, scale="row")
```
有时可能不需要行或列的聚类,原始展示就可以了。
```{r}
pheatmap::pheatmap(data, scale="row", cluster_rows=FALSE, cluster_cols=FALSE)
```
给矩阵 (`data`)中行和列不同的分组注释。假如有两个文件,第一个文件为行注释,其第一列与矩阵中的第一列内容相同 (顺序没有关系),其它列为第一列的不同的标记,如下面示例中(假设行为基因,列为样品)的2,3列对应基因的不同类型 (TF or enzyme)和不同分组。第二个文件为列注释,其第一列与矩阵中第一行内容相同,其它列则为样品的注释。
```{r}
row_anno = data.frame(type=c("TF","Enzyme","Enzyme","TF"),
class=c("clu1","clu1","clu2","clu2"), row.names=rownames(data))
row_anno
```
```{r}
col_anno = data.frame(grp=c("A","A","A","B","B"), size=1:5, row.names=colnames(data))
col_anno
```
```{r}
pheatmap::pheatmap(data, scale="row",
cluster_rows=FALSE, annotation_col=col_anno, annotation_row=row_anno)
```
自定义下颜色吧。
```{r}
# <bias> values larger than 1 will give more color for high end.
# Values between 0-1 will give more color for low end.
pheatmap::pheatmap(data, scale="row", cluster_rows=FALSE,
annotation_col=col_anno, annotation_row=row_anno,
color=colorRampPalette(c('green','yellow','red'), bias=1)(50))
```
`heatmap.2`的使用在上一期转录组分析绘制相关性热图时有提到,这次就不介绍了,跟`pheatmap`有些类似,而且也有不少教程。
## 聚类热图如何按自己的意愿调整分支顺序? {#pheatmap_branchorder}
### 数据示例 {#pheatmap_branchorderdata}
```{r}
exprTable <- read.table("exprTable.txt", sep="\t", row.names=1, header=T, check.names = F)
exprTable
```
测试时直接拷贝这个数据即可
```
## Zygote 2_cell 4_cell 8_cell Morula ICM
## Pou5f1 1.0 2.0 4.0 8.0 16.0 32.0
## Sox2 0.5 1.0 2.0 4.0 8.0 16.0
## Gata2 0.3 0.6 1.3 2.6 5.2 10.4
## cMyc 10.4 5.2 2.6 1.3 0.6 0.3
## Tet1 16.0 8.0 4.0 2.0 1.0 0.5
## Tet3 32.0 16.0 8.0 4.0 2.0 1.0
```
### 绘制一个聚类热图很简单 {#pheatmap_branchorder1}
```{r}
library(pheatmap)
pheatmap(exprTable)
```
### 如何自定义分支顺序呢 {#pheatmap_branchorder2}
自己做个`hclust`传进去,顺序跟pheatmap默认是一样的
```{r}
exprTable_t <- as.data.frame(t(exprTable))
col_dist = dist(exprTable_t)
hclust_1 <- hclust(col_dist)
pheatmap(exprTable, cluster_cols = hclust_1)
```
### 人为指定顺序排序样品 {#pheatmap_branchorder3}
按发育时间排序样品
```{r}
manual_order = c("Zygote", "2_cell", "4_cell", "8_cell", "Morula", "ICM")
dend = reorder(as.dendrogram(hclust_1), wts=order(match(manual_order, rownames(exprTable_t))))
# 默认为mean,无效时使用其他函数尝试
# dend = reorder(as.dendrogram(hclust_1), wts=order(match(manual_order, rownames(exprTable_t))), agglo.FUN = max)
col_cluster <- as.hclust(dend)
pheatmap(exprTable, cluster_cols = col_cluster)
```
### 按某个基因的表达由小到大排序 {#pheatmap_branchorder4}
可以按任意指标排序,基因表达是一个例子。
```{r}
dend = reorder(as.dendrogram(hclust_1), wts=exprTable_t$Tet3)
col_cluster <- as.hclust(dend)
pheatmap(exprTable, cluster_cols = col_cluster)
```
### 按某个基因的表达由大到小排序 {#pheatmap_branchorder5}
```{r}
dend = reorder(as.dendrogram(hclust_1), wts=exprTable_t$Tet3*(-1))
col_cluster <- as.hclust(dend)
pheatmap(exprTable, cluster_cols = col_cluster)
```
### 按分支名字(样品名字)的字母顺序排序 {#pheatmap_branchorder6}
```{r}
library(dendextend)
col_cluster <- hclust_1 %>% as.dendrogram %>% sort %>% as.hclust
pheatmap(exprTable, cluster_cols = col_cluster)
```
### 梯子形排序:最小的分支在右侧 {#pheatmap_branchorder7}
```{r}
col_cluster <- hclust_1 %>% as.dendrogram %>% ladderize(TRUE) %>% as.hclust
pheatmap(exprTable, cluster_cols = col_cluster)
```
### 梯子形排序:最小的分支在左侧 {#pheatmap_branchorder8}
```{r}
col_cluster <- hclust_1 %>% as.dendrogram %>% ladderize(FALSE) %>% as.hclust
pheatmap(exprTable, cluster_cols = col_cluster)
```
### 按特征值排序 {#pheatmap_branchorder9}
样本量多时的自动较忧排序
```{r}
sv = svd(exprTable)$v[,1]
dend = reorder(as.dendrogram(hclust_1), wts=sv)
col_cluster <- as.hclust(dend)
pheatmap(exprTable, cluster_cols = col_cluster)
```
```{r}
exprTable_cor <- cor(exprTable)
exprTable_cor
```
```{r}
pheatmap(exprTable_cor, cluster_rows = T, cluster_cols = T)
```
```{r}
cor_cluster = hclust(as.dist(1-exprTable_cor))
pheatmap(exprTable_cor, cluster_rows = cor_cluster, cluster_cols = cor_cluster)
```
```{r}
cor_sum <- rowSums(exprTable_cor)
dend = reorder(as.dendrogram(cor_cluster), wts=cor_sum)
col_cluster <- as.hclust(dend)
pheatmap(exprTable_cor, cluster_rows = col_cluster, cluster_cols = col_cluster)
```
```{r}
manual_order = c("Zygote", "2_cell", "4_cell", "8_cell", "Morula", "ICM")
dend = reorder(as.dendrogram(cor_cluster), wts=order(match(manual_order, rownames(exprTable_cor))),agglo.FUN = max)
col_cluster <- as.hclust(dend)
pheatmap(exprTable_cor, cluster_rows = col_cluster, cluster_cols = col_cluster)
```
## 箱线图 {#ggplot2boxplot}
箱线图是能同时反映数据统计量和整体分布,又很漂亮的展示图。在2014年的Nature Method上有2篇`Correspondence`论述了使用箱线图的好处和一个在线绘制箱线图的工具。就这样都可以发两篇Nature method,没天理,但也说明了箱线图的重要意义。
下面这张图展示了`Bar plot`、`Box plot`、`Volin plot`和`Bean plot`对数据分布的反应。从Bar plot上只能看到数据标准差或标准误不同;Box plot可以看到数据分布的集中性不同;Violin plot和Bean plot展示的是数据真正的分布,尤其是对Biomodal数据的展示。
Boxplot从下到上展示的是最小值,第一四分位数 (箱子的下边线)、中位数 (箱子中间的线)、第三四分位数 (箱子上边线)、最大值,具体解读参见 http://mp.weixin.qq.com/s/t3UTI_qAIi0cy1g6ZmHtwg。
```{r, echo=F}
knitr::include_graphics("images/boxplot_nm.png")
```
* Nature Method文章 <http://www.nature.com/nmeth/journal/v11/n2/full/nmeth.2811.html>
### 一步步解析箱线图绘制 {#ggplot2boxplot1}
假设有这么一个基因表达矩阵,第一列为基因名字,第一行为样品名字,想绘制样品中基因表达的整体分布。
```{r}
profile="Name;2cell_1;2cell_2;2cell_3;4cell_1;4cell_2;4cell_3;zygote_1;zygote_2;zygote_3
A;4;6;7;3.2;5.2;5.6;2;4;3
B;6;8;9;5.2;7.2;7.6;4;6;5
C;8;10;11;7.2;9.2;9.6;6;8;7
D;10;12;13;9.2;11.2;11.6;8;10;9
E;12;14;15;11.2;13.2;13.6;10;12;11
F;14;16;17;13.2;15.2;15.6;12;14;13
G;15;17;18;14.2;16.2;16.6;13;15;14
H;16;18;19;15.2;17.2;17.6;14;16;15
I;17;19;20;16.2;18.2;18.6;15;17;16
J;18;20;21;17.2;19.2;19.6;16;18;17
L;19;21;22;18.2;20.2;20.6;17;19;18
M;20;22;23;19.2;21.2;21.6;18;20;19
N;21;23;24;20.2;22.2;22.6;19;21;20
O;22;24;25;21.2;23.2;23.6;20;22;21"
```
读入数据并转换为ggplot2需要的长数据表格式,好好体会下这个格式,虽然多占用了不少空间,但是确实很方便。
```{r}
profile_text <- read.table(text=profile, header=T, row.names=1, quote="",sep=";", check.names=F)
# 在melt时保留位置信息
# melt格式是ggplot2画图最喜欢的格式
#
library(ggplot2)
library(reshape2)
data_m <- melt(profile_text)
head(data_m)
```
```{r}
summary(data_m)
```
`variable`和`value`为矩阵`melt`后的两列的名字,内部变量, 可以通过`?melt`查看如何修改。`variable`代表了点线的属性,`value`代表对应的值。
像往常一样,就可以直接画图了。
```{r}
p <- ggplot(data_m, aes(x=variable, y=value,color=variable)) +
geom_boxplot() +
theme(axis.text.x=element_text(angle=50,hjust=0.5, vjust=0.5)) +
theme(legend.position="none")
p
# 图会存储在当前目录的Rplots.pdf文件中,如果用Rstudio,可以不运行dev.off()
# dev.off()
```
箱线图出来了,看上去还可以,再加点色彩 (`fill`)。
```{r}
# variable和value为矩阵melt后的两列的名字,内部变量, variable代表了点线的属性,value代表对应的值。
p <- ggplot(data_m, aes(x=variable, y=value)) +
geom_boxplot(aes(fill=factor(variable))) +
theme(axis.text.x=element_text(angle=50,hjust=0.5, vjust=0.5)) +
theme(legend.position="none")
p
# 图会存储在当前目录的Rplots.pdf文件中,如果用Rstudio,可以不运行dev.off()
#dev.off()
```
再看看`Violin plot`
```{r}
# variable和value为矩阵melt后的两列的名字,内部变量, variable代表了点线的属性,value代表对应的值。
p <- ggplot(data_m, aes(x=variable, y=value)) +
geom_violin(aes(fill=factor(variable))) +
theme(axis.text.x=element_text(angle=50,hjust=0.5, vjust=0.5)) +
theme(legend.position="none")
p
# 图会存储在当前目录的Rplots.pdf文件中,如果用Rstudio,可以不运行dev.off()
#dev.off()
```
```{r}
# variable和value为矩阵melt后的两列的名字,内部变量, variable代表了点线的属性,value代表对应的值。
p <- ggplot(data_m, aes(x=variable, y=value)) +
geom_jitter(aes(color=factor(variable))) +
theme(axis.text.x=element_text(angle=50,hjust=0.5, vjust=0.5)) +
theme(legend.position="none")
p
# 图会存储在当前目录的Rplots.pdf文件中,如果用Rstudio,可以不运行dev.off()
#dev.off()
```
还有Jitter plot (这里使用的是ggbeeswarm包)
```{r}
library(ggbeeswarm)
# 为了更好的效果,只保留其中一个样品的数据
# grepl类似于Linux的grep命令,获取特定模式的字符串
data_m2 <- data_m[grepl("_3", data_m$variable),]
# variable和value为矩阵melt后的两列的名字,内部变量,
# variable代表了点线的属性,value代表对应的值。
p <- ggplot(data_m2, aes(x=variable, y=value),color=variable) +
geom_quasirandom(aes(colour=factor(variable))) +
theme_bw() + theme(panel.grid.major = element_blank(),
panel.grid.minor = element_blank(), legend.key=element_blank()) +
theme(legend.position="none")
p
#ggsave(p, filename="jitterplot.pdf", width=14, height=8, units=c("cm"))
```
```{r, echo=F}
knitr::include_graphics("images/boxplot_4.png")
```
也可以用`geom_jitter(aes(colour=factor(variable)))`代替`geom_quasirandom(aes(colour=factor(variable)))`绘制抖动图,但个人认为geom_quasirandom给出的结果更有特色。
### 绘制单个基因 (A)的箱线图 {#ggplot2boxplot2}
为了更好的展示效果,下面的矩阵增加了样品数量和样品的分组信息。
```{r}
#profile="Name;2cell_1;2cell_2;2cell_3;2cell_4;2cell_5;2cell_6;4cell_1;4cell_2;4cell_3;\
#4cell_4;4cell_5;4cell_6;zygote_1;zygote_2;zygote_3;zygote_4;zygote_5;zygote_6
#A;4;6;7;5;8;6;3.2;5.2;5.6;3.6;7.6;4.8;2;4;3;2;4;2.5
#B;6;8;9;7;10;8;5.2;7.2;7.6;5.6;9.6;6.8;4;6;5;4;6;4.5"
profile_text <- read.table("data/boxplot_singleGene.data", header=T, row.names=1, quote="",
sep="\t", check.names=F)
data_m = data.frame(t(profile_text['A',]))
data_m$sample = rownames(data_m)
# 只挑选显示部分
# grepl前面已经讲过用于匹配
data_m[grepl('_[123]', data_m$sample),]
```
获得样品分组信息 (这个例子比较特殊,样品的分组信息就是样品名字下划线前面的部分)
```{r}
# 可以利用strsplit分割,取出其前面的字符串
# R中复杂的输出结果多数以列表的形式体现,在之前的矩阵操作教程中
# 提到过用str函数来查看复杂结果的结构,并从中获取信息
group = unlist(lapply(strsplit(data_m$sample,"_"), function(x) x[1]))
data_m$group = group
data_m[grepl('_[123]', data_m$sample),]
```
如果没有这个规律,也可以提到类似于下面的文件,指定样品所属的组的信息。