针对近期多个粉丝咨询环形图的绘制,我意识到,我们似乎没有真正介绍过circle图,但这一类图确是非常常用的图,所以这里详细学习一下circle的绘制,使用的是circlize包,功能很完善:安装包,
#https://github.com/jokergoo/circlize/tree/master
#install package
install.packages("circlize")
#devtools::install_github("jokergoo/circlize")
library(circlize)
做环形热图使用circos.heatmap函数,我们看看具体的参数解析:
#了解下环形热图绘制circos.heatmap基本参数。
circos.heatmap(mat, #矩阵或者向量,矩阵就是和我们做hetamap的时候提供的一样,例如行是基因,列是分组sample,向量的化会自动转化为一个单列矩阵,说到底还是矩阵,适合于plot单组数值split = NULL, #环线热图分列,例如我们可以设置分组,让热图分为多个扇形col, #热图颜色设置,参照colorRamp2,设置颜色和范围,例如一般设置:colorRamp2(c(-2, 0, 2), c("blue", "white", "red"))cell.border = NA, #r热图格子边框颜色,默认NA不设置cell.lwd = 1,#边框线条宽度bg.border = NA, #cell.border设置的是每个单元格的边框,bg.border设置的是整个热图外边框颜色bg.lty = par("lty"),#整个热图边框线条类型,可以直接设置数字,例如1,2,3等等,选择是实线还是虚线类型的线条bg.lwd = par("lwd"),#整个热图边框线条宽度,可直接设置数值cluster = TRUE, #热图行是否聚类。该值也可以是一个dendprogram /hclust对象或其他可以用as. dendprogram转换为的对象。也就是所已经做好聚类的objectclustering.method = "complete", #聚类方法,参照hclust函数,可选"ward.D", "ward.D2", "single", "complete", "average" (= UPGMA), "mcquitty" (= WPGMA), "median" (= WPGMC) or "centroid" (= UPGMC).distance.method = "euclidean",#参照dist函数,method可选"euclidean", "maximum", "manhattan", "canberra", "binary" or "minkowski". # dend.callback = function(dend, m, si) reorder(dend, rowMeans(m)),dend.side = c("none", "outside", "inside"), #聚类树位置,默认不展示。可选择外圈或者内圈放置dend.track.height = 0.1,#聚类树高度rownames.side = c("none", "outside", "inside"), #行名位置,默认不展示。可选择外圈或者内圈放置rownames.cex = 0.5,#行名文字大小rownames.font = par("font"), #行名字体rownames.col = "black",#行名颜色cell_width = rep(1, nrow(mat)),#热图单元格宽度
准备作图数据,这里我们使用转录组基因表达矩阵演示:
#prepare data
#这是一个转录组基因表达矩阵,也是我们常做pheatmap的输入矩阵
#行是基因,列是分组,有两个分组,HC,TL,分别有6个重复
df <- read.csv('./df_heat.csv', header = T, row.names = 1)
plot_df <- as.matrix(df)
#因为circos.heatmap没有像pheatmap那样,直接参数里面对数据进行scale
#所以我们plot之前需要对表达数据进行标准化,如果仅仅是想展示数值,那么数据并不用标准化
plot_df <- t(scale(t(plot_df)))#对数据进行标准化处理,按行z-score标准化
基础plot:布局也是绘图关键哦,了解下布局一些参数,可以调整:不可死板。
circos.par(start.degree,#数值,设置环形图的起始角度。0是其实,位于3点钟方向,数值增大,开口逆时针移动gap.degree,#数值或数值向量,设置扇区之间的间隔角度.单个数值设置表示多个扇区之间距离一致。向量可设置不一样间隔gap.after, #设置圆环开口的大小,数值越大,开口越大track.margin,#数值向量,设置轨道的上下边距cell.padding,#设置单元格的内边距,扇形内格子之间的距离,一般设置为0track.height,#设置轨道的高度,也就是圆环的宽度,默认0.2clock.wise,#设置环形方向是否为顺时针,默认Tlabels.cex,#设置文字大小labels.font,#设置文字字体,1=普通,2=粗体,3=斜体,4=粗斜体)unit.circle.segments,#设置绘制圆时的线段数量(影响圆形的平滑度),默认500big.gap,#设置两个环形图之间的间隔角度(用于多环形图)circle.margin,#环形图布局,图形距离四边的距离,数值向量设置。..., RESET = FALSE, READ.ONLY = NULL, LOCAL = FALSE, ADD = FALSE)
#布局设置
circos.clear()
circos.par(start.degree=30, gap.degree=30,track.height=0.3)circos.heatmap(plot_df,col = colorRamp2(c(-2,0,2),c("#003399","white","#cccc00")), cell.border = NA, cell.lwd = 1,bg.border = 'black', bg.lty = par("lty"),bg.lwd = par("lwd"),cluster = TRUE, clustering.method = "ward.D2", distance.method = "euclidean",dend.side = c("inside"), dend.track.height = 0.1,rownames.side = c("outside"),rownames.cex = 0.5,#行名文字大小rownames.font = par("font"), #行名字体rownames.col = "black")
基础图形出来了,感觉把平常的热图给掰弯了,现在还缺少legend和sample名:添加是比较麻烦的,需要为整个轨道创建绘图区域,使用函数circos.track():添加文字使用circos.text,列名的位置需要根据自己的实际数据,调整x,y轴的坐标慢慢实现,如果感觉调整费时间,可以导出手动修饰,这也是circlize的一个不方便之处。
circos.track(track.index = get.current.track.index(), # 指定当前轨道的索引panel.fun = function(x, y) { #panel.fun,自定义绘制函数,用于在轨道中添加图形或注释if (CELL_META$sector.numeric.index == 1) { # 仅在第一个扇区执行cn = colnames(plot_df) # 获取数据框的列名n = length(cn) # 列名的数量#添加文字注释,参数需要设置文字位置xy坐标circos.text(x = CELL_META$cell.xlim[2] + convert_x(0.8, "mm"),#x坐标y = CELL_META$cell.ylim[2] + 4.5 * (1:n), # y 坐标,labels = rev(cn), # 文本内容(列名),这里需要注意,rev一下,保证标注正确cex = 0.4, # 字体大小facing = "inside" # 文本方向(朝内))}},bg.border = NA # 轨道背景无边框
)
然而,我们前面也介绍了,circos.heatmap有一个参数,split,热图可以按照行的分组split,这里演示split以及添加分组注释。其他和之前一致。我的数据并没有分组,这里随机设置的,没有任何意义。将基因按照功能进行了分组。
annotation_row=data.frame(Pathway=c(rep("Wnt",20),rep("Inflammatory",32),rep("HIF",34)))#对行进行注释,用于后续的热图分裂
row.names(annotation_row) <- rownames(df)
annotation_row <- as.matrix(annotation_row)#在circlize函数中,需要为matrix#plot,画图和之前一样,需要注意的是布局设置的gap.degree
#这里我们有三组,也就是split时出现三组扇区,前面两个扇区的gap设置小点
#最后一个设置大一点,便于添加列名
circos.clear()
circos.par(start.degree=30, gap.degree=c(2,2,30),track.height=0.3)
circos.heatmap(plot_df,split = annotation_row,col = colorRamp2(c(-2,0,2),c("#003399","white","#cccc00")), cell.border = NA, cell.lwd = 1,bg.border = 'black', bg.lty = par("lty"),bg.lwd = par("lwd"),cluster = TRUE, clustering.method = "ward.D2", distance.method = "euclidean",dend.side = c("inside"), dend.track.height = 0.05,rownames.side = c("outside"),rownames.cex = 0.5,#行名文字大小rownames.font = par("font"), #行名字体rownames.col = "black",dend.callback=function(dend,m,si){color_branches(dend,k=5,col=c("#E69253", "#EDB931", "#E4502E", "#4378A0", "#272A2A"))})#还可以对层次聚类添加颜色,按照聚类数#
circos.track(track.index = get.current.track.index(), # 指定当前轨道的索引panel.fun = function(x, y) { #panel.fun,自定义绘制函数,用于在轨道中添加图形或注释if (CELL_META$sector.numeric.index == 3) { # 仅在第一个扇区执行cn = colnames(plot_df) # 获取数据框的列名n = length(cn) # 列名的数量#添加文字注释,参数需要设置文字位置xy坐标circos.text(#这里我们将列名绘制在环形的尾部,我们前面参数里面解释了,环形是默认顺时针绘制的x = CELL_META$cell.xlim[2] + convert_x(0.8, "mm"),#前面的部分是获取行的长度,也就是绘制文字的x坐标,后面convert_x是调整参数,0.5表示文本向右偏移0.5y = CELL_META$cell.ylim[2] + 12 * (1:n), # y 坐标,labels = rev(cn), # 文本内容(列名),这里需要注意,rev一下,保证标注正确cex = 0.4, # 字体大小facing = "inside" # 文本方向(朝内))}},bg.border = NA # 轨道背景无边框
)#添加分组设置,我们直接接上一个图层plot,这里没有设置track.index,所以注释在内圈
circos.track(ylim=c(0,1),track.height=0.081,#分组注释环形高度,可以设置低一点bg.col=c("#FF0000", "#00A08A", "#F2AD00"),#分组注释背景颜色panel.fun=function(x, y) {xlim = get.cell.meta.data("xlim")ylim = get.cell.meta.data("ylim")sector.index = CELL_META$sector.indexcircos.text(mean(xlim),mean(ylim),sector.index, col = "black", cex = 0.8, facing = 'bending.inside', niceFacing = TRUE)})