来吧,自定义你的热图

作者: 刘小泽 | 来源:发表于2020-01-09 18:18 被阅读0次

    刘小泽写于19.1.9
    之前写过一篇:自己动手,丰衣足食~改造inferCNV的热图 通过调取inferCNV的数据,改造了它的热图代码;
    后来看它没有样本注释,于是又写了:热图~给样本分组上个色

    前言

    通过前两次探索,能做出来这样的图:

    图1

    但总感觉怪怪的,灰蒙蒙的背景给心灵笼罩了一层乌云...

    按说这个(图1)应该反映的是CNV情况,那么能不能只存在红、蓝、白三种颜色呢?

    并且看到官方给出的一个(图2)是:

    图2

    这次,我们来继续探索强大的ComplexHeatmap

    如何设定热图显示的颜色?

    在官方说明的:https://jokergoo.github.io/ComplexHeatmap-reference/book/heatmap-annotations.html

    其中说明了可以用circlize::colorRamp2()函数来设定

    就以官方给出的这个热图(图2)为例,我们看到它的表达量范围是在:0.4(蓝色)——1.6(红色),并且大部分为1(为白色)

    那么先对表达矩阵进行这样一个处理,为了后面尽可能凸显蓝色和红色:

    n[n>1.6]=1.6
    n[n<0.4]=0.4
    n[0.8<n & n<1.2]=1
    

    然后指定颜色及对应的数值:

    library("circlize")
    col_fun = colorRamp2(c(0.4, 1, 1.6), c("blue", "white", "red"))
    

    最后这个col_fun就可以丢到Heatmap中,指定col = col_fun

    整个的作图代码是:

      ht_tumor = Heatmap(as.matrix(n),
                         name="ht_tumor", #这个一定要命名,因为下面画线要用到
                         col = col_fun,
                         cluster_rows = F,
                         #clustering_method_rows = 'ward.D2',
                         cluster_columns = F,
                         show_column_names = F,
                         show_row_names = F,
                         column_split = new_cluster,
                         column_gap = unit(0, "mm"), #注意这里我设成了0,也是为了后面画线
                         heatmap_legend_param = list(
                           title = "Modified Expression",
                           title_position = "leftcenter-rot", # 图例标题位置
                           at=c(0.4,0.8,1,1.2,1.6), #图例范围
                           legend_height = unit(3, "cm") #图例长度
                         ),
                         top_annotation = top_color,
                         row_title = "Observations (Cells)",
                         row_title_side = c("right"),
                         row_title_rot = 90,
                         column_title = "Genomic Region",
                         column_title_side = c("bottom"),
                         left_annotation=rowAnn)
      draw(ht_tumor, heatmap_legend_side = "left") # 图例位置
    
    图3

    之前画的热图(图1)都是用间隔来区分不同的chr,但当时由于大部分背景是灰色,所以放大后看间隔,还算清晰。

    当我们把背景颜色调成了白色,就引出一个问题:各个染色体的分隔区域看不出来了

    如何在热图上画线?

    这个问题在图3中是区分染色体,但换一种,也是适用的。就是说,指定了列的分隔,但又想让它清晰可见。那么画线是一个不错的选择(就像官方示例——图2一样)

    可以参考:https://jokergoo.github.io/ComplexHeatmap-reference/book/heatmap-decoration.html

    举个例子:下面左边👈是最初的热图,然后利用一个函数decorate_heatmap_body就可以变成右边👉这种:

    set.seed(123)
    Heatmap(matrix(rnorm(100), 10), name = "mat") # 看到name参数的作用了吧
    
    decorate_heatmap_body("mat", {
      #在x轴上画线
      grid.lines(c(0.5, 0.5), c(0, 1), gp = gpar(lty = 2, lwd = 2))
      #在y轴上画线
      grid.lines(c(0, 1), c(0.1, 0.1), gp = gpar(lty = 2, lwd = 2))
    })
    

    但有个难点,就是这两个参数不知道怎么设置:

    思考、反复尝试了多次,我才弄懂了这两个参数的意思

    • 先要知道,第一个c()就是指定x轴,第二个指定y轴
    • 一个热图的区间范围是0—1
    • 然后以c(0.5, 0.5), c(0, 1)为例,说明起点是0.5,终点也是0.5;而y轴起点是0(底部),终点是1(顶部),那么也就是从x轴一半的位置从下往上画一条线

    搞明白了两个参数,接下来就是想办法区分热图上的各个染色体

    可以看到我们有这么多染色体小区间,那么怎么计算画线的位置呢?

    > table(sub_geneFile$V2)
    
     chr1 chr10 chr11 chr12 chr13 chr14 chr15 chr16 chr17 chr18 chr19  chr2  chr3  chr4  chr5  chr6  chr7  chr8  chr9 
      811   645  1108   459   488   452   532   426   681   365   472  1108   682   873   871   675  1024   710   784
    

    思考🤔:

    • 要画x轴上的分割线,就要得到每个区间右侧的坐标;
    • 因为之前把各个chr进行了column_split,所以画图会认为每个分开的chr为一个单独的区间;
    • decorate_heatmap_body会以第一个chr1为基准,它的范围是[0,1]。要得到之后各个chr的右侧坐标,就要先累加,然后除以第一个chr1的长度;
    • 前提是每个单独的区间之间不存在间隔,因此前面把column_gap设成了0
    首先得到每个染色体在热图上的区间长度
    chr_info <- as.numeric(table(sub_geneFile$V2))
    > chr_info
     [1]  811  645 1108  459  488  452  532  426  681  365  472 1108  682  873  871  675 1024  710  784
    
    然后累加,并除以第一个chr1的数值
     x=cumsum(chr_info)/chr_info[1]
    > x
     [1]  1.000000  1.795314  3.161529  3.727497  4.329223  4.886560  5.542540  6.067818  6.907522  7.357583  7.939581
    [12]  9.305795 10.146732 11.223181 12.297164 13.129470 14.392109 15.267571 16.234279
    
    然后自定义一个函数draw_x_line,传递给sapply,给每个chr画图
    draw_x_line=function(x,ht1,color){
        decorate_heatmap_body(ht1, {
          grid.lines(c(x, x), c(0, 1), gp = gpar(lty = 2, lwd = 2, col=color))
        })
      }
    
    sapply(x, function(x)draw_x_line(x,ht1="ht_tumor",color="black"))
    

    小Tip:

    如果前面Heatmap()中没指定name="ht_tumor",而且一定要先做出来热图,再往上面画线
    就会出现这样的报错

    搞定了x轴上的线,那么y轴也不难了

    思考🤔:

    • y轴和x轴不同,它是作为一整个区间,所以不需要像x轴一样去累加,从上到下就是[0,1];
    • 又因为y轴是从下往上画,所以顺序是tumor=》mut=》control。只需要得到两条线的位置即可(也就是下面的y_dat
    • 另一个坑就是:grid.lines中的第一个指定x不能是c(0, 1),这样只会画出chr1中一条短短的线,要得到所有chr的总长度,也就是存储在之前cumsum计算的x的最后一个值
    看代码:
    # 先得到y轴展示的信息
    type_info=as.numeric(table(ann$type))
    > type_info
    [1] 165 144 168
    # 反转顺序
    type_info=rev(type_info)
    y_dat=cumsum(type_info/sum(type_info))[1:2]
    > y_dat
    [1] 0.3459119 0.6477987
    
    注意那个坑
    # 红线正确,【注意使用了c(0, x[length(x)]) ,而不是c(0,1)】
    decorate_heatmap_body('ht_tumor', {
      grid.lines(c(0, x[length(x)]),c(0.3459119, 0.3459119), gp = gpar(lty = 2, lwd = 2, col='red'))
    })
    # 蓝线错误
    decorate_heatmap_body('ht_tumor', {
      grid.lines(c(0, 1),c(0.6477987, 0.6477987), gp = gpar(lty = 2, lwd = 2, col='blue'))
    })
    

    小结

    经过了11次的反复调整代码、试错,最终画出了

    不管怎么说,对画图的参数设置和函数使用理解又加深了一步🤓

    好啦,花花说饿了,我们要去吃饭🍚啦!


    欢迎关注我们的公众号~_~  
    我们是两个农转生信的小硕,打造生信星球,想让它成为一个不拽术语、通俗易懂的生信知识平台。需要帮助或提出意见请后台留言或发送邮件到jieandze1314@gmail.com

    Welcome to our bioinfoplanet!

    相关文章

      网友评论

        本文标题:来吧,自定义你的热图

        本文链接:https://www.haomeiwen.com/subject/tgfuactx.html