用ComplexHeatmap画热图,列名竖向排列
软件版本
- R版本:version 3.6.0
- R包版本:ComplexHeatmap 2.2.0
需求说明
列名放在热图上方,字体排列是竖向的。
- 列名放在热图上方很好解决,直接用Heatmap()的column_names_side = “top”。
- 问题是字体竖向排列。
失败方案
方案1
Heatmap(column_names_rot = 90),不行。
列名是整体旋转,做不到竖向阅读。
方案2
列名插入换行符,不行。
可以竖向阅读,但是列名会和热图区域重叠。
解决方案
列名插入换行符,并用decorate_dimnames()重绘列名组件。
热图示例
数据示例
文本文件的中文编码转换示例
iconv -c -f GBK -t UTF-8 Input.GBK.tsv -o Output.utf8.tsv
代码
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# ComplexHeatmap参考示例
# https://jokergoo.github.io/ComplexHeatmap-reference/book/
library(ComplexHeatmap)
library(circlize)
args <- commandArgs(T)
Input_Table = args[1]
Output_Dir = args[2]
# 如果输出目录不存在,创建它
if (! dir.exists(Output_Dir)){
dir.create(Plot_Result_Dir)
}
# 输出图片文件路径
Output_Pic = paste(Output_Dir, "/Heatmap.png", sep = "", collapse = "")
########################## 数据处理 ##########################
# 读入数据
# stringsAsFactors=FALSE 不将字符型变量转为因子,否则对其做字符串操作会报错
# check.names=FALSE 不检查列名的命名有效性,否则列名会被修改:如数字开头会在最前面被加上X、空格被变为.、-被变为.等情况
# check.names=FALSE后,列名的空格会被保留,用列名获取数据时,需要用Data_Frame$`Name With Space`这种格式
Data = read.table(Input_Table, head = T, sep = "\t", comment.char = "#", stringsAsFactors = FALSE, check.names = FALSE)
# 函数:字符串的每个字符中间插入换行符
wrap_letters <- function(x) {
sapply(strsplit(x, ''), paste0, collapse = '\n')
}
# Data从第2列到最后1列作为画图数据Data_Draw
Data_Draw = Data[,2:ncol(Data)]
# Data的第1列作为画图数据Data_Draw的行名
row.names(Data_Draw) = Data[,1]
# 另存画图数据Data_Draw的列名,并在列名的每个字符中间插入换行符
Col_Names = wrap_letters(colnames(Data_Draw))
# 将Data_Draw的列名替换为空
names(Data_Draw) = rep("", ncol(Data_Draw))
# 用于cell显示数值。read.table(colClasses = "character")数值全部作为字符串读入,避免format()转换的科学计数法。NA转为空字符串。
Cell_Number = read.table(Input_Table, head = T, sep = "\t", comment.char = "#", stringsAsFactors = FALSE, check.names = FALSE, colClasses = "character", na.strings = "")
Cell_Number = Cell_Number[,2:ncol(Cell_Number)]
Cell_Number[is.na(Cell_Number)] = ""
########################## 画图 ############################
# 根据画图数据的行数、列数设置图的尺寸
base_size = 3
hight_factor = ceiling(nrow(Data_Draw) / 8)
width_factor = ceiling(ncol(Data_Draw) / 4)
width_value = base_size * width_factor * 0.6
if(width_value > 200){width_value = 200}
height_value=(base_size + 2) * hight_factor * 0.65
if(height_value > 300){height_value = 300}
png(Output_Pic, width = width_value, height = height_value, units = "in", res=300)
# 热图大小占图的80%
Heatmap_Width = width_value * 0.8
Heatmap_Height = height_value * 0.8
# 自定义热图颜色
Color_Palette <- colorRamp2(c(0, 25, 50, 100), c("white", "green", "yellow", "red"))
# 热图
Darw_Heatmap = Heatmap(
Data_Draw,
name = "Darw_Heatmap",
width = unit(Heatmap_Width, "in"),
height = unit(Heatmap_Height, "in"),
col=Color_Palette,
# 行、列聚类
cluster_rows = FALSE,
cluster_columns = FALSE,
# 行名相关参数
show_row_names = TRUE,
row_names_side = "right",
row_names_gp = gpar(fontsize = 12),
row_names_max_width = max_text_width(row.names(Data_Draw), gp = gpar(fontsize = 12)),
row_names_rot = 0,
# 列名相关参数
show_column_names = TRUE,
column_names_side = "top",
column_names_gp = gpar(fontsize = 12),
column_names_max_height = max_text_width(names(Data_Draw), gp = gpar(fontsize = 12)),
column_names_rot = 0,
# 相当于pheatmap的border_color和border线宽度
rect_gp = gpar(col = "grey", lwd = 1),
# 每个cell显示对应数值,相当于pheatmap的display_numbers
cell_fun = function(j, i, x, y, width, height, fill) {grid.text(Cell_Number[i,j], x, y, gp = gpar(fontsize = 9))},
# 空值的cell的颜色
na_col = "white",
# 图例相关参数
show_heatmap_legend = FALSE,
heatmap_legend_param = list(title = "", color_bar = "continuous")
)
# 图例
Draw_Legend = Legend(col = Color_Palette, title = "", grid_height = unit(6, "mm"), grid_width = unit(6, "mm"))
# 设置热图四周的距离,因为有的列名很长,热图上方需要空出更多空间
Heatmap_Left_Padding = 2
Heatmap_Bottom_Padding = 2
Heatmap_Top_Padding = 94
Heatmap_Right_Padding = 2
# 绘制热图和图例
# 热图需要padding,图例要移动位置,所以必须用draw()
draw(Darw_Heatmap, padding = unit(c(Heatmap_Bottom_Padding, Heatmap_Left_Padding, Heatmap_Top_Padding, Heatmap_Right_Padding), "mm"))
draw(Draw_Legend, x = unit(0.98, "npc"), y = unit(0.85, "npc"), just = c("right", "top"))
# 根据热图padding设置第一个列名的位置
X_Pos = unit(Heatmap_Left_Padding+1, "mm")
# 每个列名输出后需要移动一定位置,以对齐每个cell。根据热图大小和列数量,计算每个cell的宽度
X_Step = unit(Heatmap_Width/ncol(Data_Draw), "in")
# decorate_*()系列函数用于绘制热图后,对热图组件做修改 https://jokergoo.github.io/ComplexHeatmap-reference/book/heatmap-decoration.html
# decorate_dimnames(which = "column")用于修改列名组件
# 注1:decorate_*()必须在热图绘制后才能用。如果Darw_Heatmap = Heatmap(...)后,必须先draw(Darw_Heatmap);或者不把Heatmap(...)赋给变量和draw()。同理,如果要修改某个组件,该组件必须先被绘制出来;我要修改列名,那必须Heatmap(show_column_names = TRUE),所以前面要将Data_Draw的列名设为空值,否则Heatmap()和decorate_dimnames()会导致两次的列名重叠。
# 注2:decorate_*()的[heatmap]参数,是name of the heatmap,所以要用Heatmap(name = "xxx")填的热图名称,而不是Heatmap(...)赋给的变量名
# 注3:这里Col_Names是直接取Data_Draw的列名。如果热图有做列聚类,列名顺序可能改变了,注意修改。
decorate_dimnames("Darw_Heatmap", which = "column", {
for (Name in Col_Names){
# 向下对齐,设置横坐标
grid.text(Name, just = "bottom", x = X_Pos)
# 列名输出后移动位置
X_Pos = X_Pos + X_Step
}
})
dev.off()