什么是弦图(Chord Diagram),其实也是圈图的一种,感觉就是圈图,并且看上去还是比较简单的圈图,就外围的track加上links。
弦图是一种展示数据之间相互关系的图形。弦图中的数据点以圆的形式呈放射状排列,并用线条来展示数据之间的联系。在弦图中,我们可以通过颜色和线条的粗细来展现不同类型联系和强度。这种联系有多种形式比如相关性,比如存在与否,比如迁入迁出等。
那么让我们先来看几个弦图的例子。
下面的弦图就展现了不同的OTU在不同环境的存在情况。比如,研究发现OTU1存在于酸性矿山排水(AMD)、矿山(Mine)、河流(Riverine)、火山(Volcanic)等多个环境,其中在酸性矿山排水中的存在最多(连线最宽)。而关注不同的环境可以发现酸性矿山排水中主要存在OTU1、OTU5、OTU6、OTU7、OTU8、OTU20等微生物。
包括我最近常用的来描述不同细胞类型互作的展示。
和基因组上面的展示一样,当数据点不是很多的时候,弦图能很直观地展现出不同数据点之间的关系。但是当数据点过多的时候,可能弦图看起来就有一些混乱了,所以还是取决于你想用图去表达什么结论。
关于测试数据,我们要使用的数据来自于“migest”这个包。所以我们先安装该包然后读取数据。
我们要用的绘图工具是来自“circlize”包的chordDiagram()函数,这个函数更像是我们上一篇介绍的circos.track和circos.link的复合体。
我们要准备的数据具体分为2部分,一部分是用于作图的具体移民数据,还有一部分是调整作图参数的文件。
library(tidyverse)
library(circlize)
library(migest)
d0 <- read_csv(system.file("imr", "reg_flow.csv", package = "migest"))
d0
# d0中包含了很多年份的人口迁移情况;主要关注三列!
# 原来的地方 --> 迁移的地方 --> 迁移的流量有多大
下面就是参数文件,也是migest里面自带的。
d1 <- read_csv(system.file("vidwp", "reg_plot.csv", package = "migest"))
d1主要包含一些配置信息:例如每个条带的颜色信息,以及条带的顺序信息!其实一般主要的就是这个。
我们下面开始试着画这个图。
chordDiagram(x = test)
下面进行一些参数的调整。
chordDiagram(x = test,
order = d1$region, # 按照d1表中的顺序排;
grid.col = d1$col1, # 按照d1表中的颜色设定;
)
调整程我们先要的颜色和顺序。比如是从Northern America,Africa,Europe。。。顺序一次显示,颜色也设置成相应的d1文件中的颜色。
我们也可以选择展示标签、刻度、方格。
annotationTrack=c("axis","grid") 去标签
annotationTrack=c("name","grid") 去刻度
annotationTrack=c("name","axis") 去方格
annotationTrack = "grid" 去除标签和刻度
chordDiagram(x = test,
order = d1$region, # 按照d1表中的顺序排;
grid.col = d1$col1, # 按照d1表中的颜色设定;
annotationTrack = c("name","grid"), #选择展示标签和刻度,这个就去除刻度
)
下面就是去掉刻度的情况
当然还有很多其它参数:
df : 至少有两列的数据帧。前两列指定连接,第三列(可选)包含映射到链接宽度的数值以及颜色(如果将col指定为颜色映射)功能地块中的扇区将为并集(df$$1¥,df$$2¥)。
grid.border : 网格的边框。如果为空,则边框颜色与网格颜色相同
transparency : 链接颜色的透明度,0表示没有透明度,1表示完全透明透明度。如果透明度已在列或中设置行.列或者列.col,此参数将已忽略。NAalso忽略此参数。
col : 链接的颜色。它可以是与df中的连接相对应的向量,也可以是根据df中的值(第三列)生成颜色的函数,也可以是表示所有链接的颜色相同的单个值。可以使用colorRamp2生成一个函数,将值映射到颜色。
directional : 链接是否有方向。1表示方向从df中的第一列到第二列,-1表示相反方向,0表示无方向,2表示双向。该值可以是与df中的行数长度相同的向量。
xmax : x轴上的最大值,该值应为命名向量。
direction.type : 表示方向的类型。可以是“diffHeight”和“arrows”中的一个或两个值。如果该值包含“diffHeight”,则使用链接的不同高度来表示起始根具有较长高度的方向,以使人们感觉到有东西在发生。如果该值包含“arrows”,则用户可以使用以下命令自定义箭头争论。争论值可以是与df中的行数具有相同长度的向量。注意:如果要为某些链接同时设置diffheight和arrows,则需要将这两个选项嵌入一个字符串中,例如“高度+箭头".
diffHeight : 如果directive设置为TRUE,则两个“根”之间的高度差。如果该值设置为正值,则起始根比结束根短;如果该值设置为负值,则起始根比结束根长。该值可以是与df中的行数长度相同的向量。
link.target.prop : 如果弦图是方向图,对于每个源扇区,是否绘制显示目标扇区比例的条形图。
target.prop.height : 钢筋的高度link.target.prop链接已打开。
reduce : 如果某个网格的宽度与整个圆的宽度之比小于此值,则会在上删除该网格绘图集如果你想保持所有的小网格,它的值应该小于零。
self.link : 如果一个扇区中存在自链接,1表示链接将退化为“山”,宽度与此连接的值相对应。2表示起始根和结束根的宽度都与连接的值相对应。
preAllocateTracks : 在绘制弦图之前预先分配空音轨。它可以是一个数字,表示需要创建多少空磁道,也可以是一个包含空磁道设置的列表。有关详细信息,请参阅渐晕图。
annotationTrack : 应该绘制哪个注释轨迹?默认情况下,将创建包含扇区名称的轨迹和包含栅格的轨迹。
annotationTrackHeight : 轨迹高度与annotationTrack中的值相对应。
link.border : 链接的边框,单个标量或与df或数据帧的nrows长度相同的向量
link.lwd : 链接边界的宽度,单个标量或与df或数据帧的nrows长度相同的向量
link.lty : 链接边框样式,单个标量或与df或数据帧的nrows长度相同的向量
link.auto : 忽略。
link.sort : 是否根据每个扇区上链接的宽度对其进行排序。如果设置为“总体”,则所有链接都将按顺序排序,无论它们来自第一列还是第二列。
link.decreasing : 为了链接.排序
link.arr.length : 传给循环链接. 此参数的格式与链接.lwd.
link.arr.width : 传给箭头。此参数的格式与链接.lwd.
link.arr.type : 传给循环链接,设置与相同链接.lwd. 默认值为三角形。
link.arr.col : 颜色或放在皮带中心的单线连接。此参数的格式与链接.lwd.
link.arr.lwd : 位于皮带中心的单线连接的线宽。此参数的格式与链接.lwd.
link.arr.lty : 位于皮带中心的单线链环的线型。此参数的格式与链接.lwd.
link.largest.ontop : 控制添加链接的顺序,是否基于绝对值?
link.rank : 此参数已删除。
link.visible : 是否绘制链接。该值是逻辑值,如果设置为FALSE,则不会绘制相应的链接,但仍会占用空间。此参数的格式与链接.lwd
link.zindex : 为了添加到圆的链接,一个大的值意味着以后添加它。
link.overlap : 如果是方向弦图,那么在同一扇区中出现或结束的链路是否重叠?
scale : 将每个扇区缩放到相同的宽度
group : 它包含组标签,扇区名称用作向量中的名称。
big.gap : 数据框第一列扇区和第二列扇区之间的间距。
small.gap : 行之间的差距很小。
所以我们来精调一下这个图的显示:
chordDiagram(x = test,
order = d1$region, # 按照d1表中的顺序排;
grid.col = d1$col1, # 按照d1表中的颜色设定;
annotationTrack = c("name","grid"), #选择展示标签和刻度,这个就去除标签
transparency = 0.25,# 线条的透明度,0表示完全不透明,被压住的颜色就看不见了;1表示完全透明
annotationTrackHeight = c(0.03, 0.1),# 外面一圈的宽度:改变第一个值
directional = 1, # 表示线条的方向,0代表没有方向,1代表正向,-1代表反向,2代表双向
direction.type = c("diffHeight","arrows"), # 线条是否带有箭头
# link.arr.type = "big.arrow", #箭头的类型
link.arr.type ="triangle",
diffHeight =0.04#外圈和中间连线的间隔,正负来调控起始和结束之间的高度
)
大部分单细胞通讯的展示,基本都是采用下面的和这个样式。
但是,从图中我们可以看出,有些label偏长的时候,显示的很不好看,所以需要修改一下比如过长需要分行展示,但是chordDiagram没办法实现和细修,这样子的情况下,我们就需要去AI里面自己调整。或者我们也可以不用默认显示的label,自己通过circlize包的circos.track来添加label和axis。
chordDiagram(x = test,
order = d1$region, # 按照d1表中的顺序排;
grid.col = d1$col1, # 按照d1表中的颜色设定;
annotationTrack = c("grid"), #选择展示标签和刻度,这个就去除标签和刻度
transparency = 0.25,# 线条的透明度,0表示完全不透明,被压住的颜色就看不见了;1表示完全透明
annotationTrackHeight = c(0.03, 0.1),# 外面一圈的宽度:改变第一个值
directional = 1, # 表示线条的方向,0代表没有方向,1代表正向,-1代表反向,2代表双向
direction.type = c("diffHeight","arrows"), # 线条是否带有箭头
# link.arr.type = "big.arrow", #箭头的类型
link.arr.type ="triangle",
diffHeight =0.04#外圈和中间连线的间隔,正负来调控起始和结束之间的高度
)
我们也可以不添加label,而是放到legend里面去。
pdf("test1.pdf", width=9, height=5, pointsize=8)
chordDiagram(x = test,
order = d1$region, # 按照d1表中的顺序排;
grid.col = d1$col1, # 按照d1表中的颜色设定;
annotationTrack = c("grid","axis"), #选择展示扇形和刻度,而去除了标签
transparency = 0.25,# 线条的透明度,0表示完全不透明,被压住的颜色就看不见了;1表示完全透明
annotationTrackHeight = c(0.03, 0.1),# 外面一圈的宽度:改变第一个值
directional = 1, # 表示线条的方向,0代表没有方向,1代表正向,-1代表反向,2代表双向
direction.type = c("diffHeight","arrows"), # 线条是否带有箭头
# link.arr.type = "big.arrow", #箭头的类型
link.arr.type ="triangle",
diffHeight =0.04#外圈和中间连线的间隔,正负来调控起始和结束之间的高度
)
legend("right",pch=20,legend=d1$region,col=d1$col1,bty="n",cex=1,pt.cex=3,border="black")
dev.off()
当然,如果我们非要让标签显示的好看点的话,就需要好好控制了,特别长的需要换行。从d1中的数据也可以看出,我们把特别长的分成了2部分,reg1和reg2。
pdf("test2.pdf", width=14, height=12)
chordDiagram(x = test,
order = d1$region, # 按照d1表中的顺序排;
grid.col = d1$col1, # 按照d1表中的颜色设定;
annotationTrack = c("grid"), #选择展示标签和刻度,这个就去除标签和刻度
transparency = 0.25,# 线条的透明度,0表示完全不透明,被压住的颜色就看不见了;1表示完全透明
annotationTrackHeight = c(0.03, 0.1),# 外面一圈的宽度:改变第一个值
directional = 1, # 表示线条的方向,0代表没有方向,1代表正向,-1代表反向,2代表双向
direction.type = c("diffHeight","arrows"), # 线条是否带有箭头
# link.arr.type = "big.arrow", #箭头的类型
link.arr.type ="triangle",
diffHeight =0.04#外圈和中间连线的间隔,正负来调控起始和结束之间的高度
)
# 添加labels and axis,相当于我们添加了两次label,稍微长的,又添加了一次
circos.track(track.index = 1, bg.border = NA,
panel.fun = function(x, y) {
xlim = get.cell.meta.data("xlim")# 获取x轴的刻度范围:
sector.index = get.cell.meta.data("sector.index")# 获取原本所在的扇形区域
reg1 = d1 %>% filter(region == sector.index) %>% pull(reg1) # reg1:获取目标州名的第一部分;
reg2 = d1 %>% filter(region == sector.index) %>% pull(reg2) # reg2:获取目标州名的第二部分(有些为NA);
circos.text(x = mean(xlim), y = ifelse(is.na(reg2), 3.5, 5.5),labels = reg1, facing = "bending", cex =1.2) # 添加洲名:这里添加了两次,非常好的操作!第一次执行判断:ifelse(is.na(reg2), 2, 3) 如果reg2不是NA,reg1就画的矮一点;反之reg1画的高一点;
circos.text(x = mean(xlim), y = 3.5, labels = reg2,facing = "bending", cex = 1.2) # 第二次直接画reg2,放在矮点的位置;
circos.axis(h = "top", labels.cex = 0.8, labels.niceFacing = FALSE,labels.pos.adjust = FALSE) # 绘制刻度线:
})
dev.off()