热图竖向列名

用ComplexHeatmap画热图,列名竖向排列

软件版本

  • R版本:version 3.6.0
  • R包版本:ComplexHeatmap 2.2.0

需求说明

列名放在热图上方,字体排列是竖向的。

  • 列名放在热图上方很好解决,直接用Heatmap()的column_names_side = “top”。
  • 问题是字体竖向排列。

失败方案

方案1

Heatmap(column_names_rot = 90),不行。
列名是整体旋转,做不到竖向阅读。
热图1column_names_rot=90

方案2

列名插入换行符,不行。
可以竖向阅读,但是列名会和热图区域重叠。
热图2列名插入换行符

解决方案

  • 列名插入换行符,并用decorate_dimnames()重绘列名组件。

  • 热图示例
    热图3完成

  • 数据示例
    数据示例

  • 文本文件的中文编码转换示例
    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()