circlize 之 chordDiagram 函数高级用法
点击上方关注我们
The end ...
这应该是 circlize 的最后部分了吧,欢迎一直关注我的小伙伴们陪着我一直学习这个包,也希望大家能够有收获并且绘制出自己好看的环形图!
昨天介绍了 chordDiagram 函数大部分的功能参数等,基本上对于大部分绘图已经足够,再介绍一些进阶用法方便做出更 复杂高级 的和弦图。
1、轨道设置
默认情况下,chordDiagram()
创建两个轨道,一个轨道用于 标签,另一个轨道用于带轴的网格 :
chordDiagram(mat)
circos.info()
## All your sectors:
## [1] "S1" "S2" "S3" "E1" "E2" "E3" "E4" "E5" "E6"
##
## All your tracks:
## [1] 1 2
##
## Your current sector.index is E6
## Your current track.index is 2
这两个轨道可以由 annotationTrack
参数控制。此参数的可用值有 grid、name 和 axis。注释轨道的高度可以通过 annotationTrackHeight
来设置,该值为单位圆半径的百分比,可以通过 mm_h()
函数来设置,且以绝对单位为单位。只有在 annotationTrack
中设置了网格时才会添加坐标轴:
chordDiagram(mat, grid.col = grid.col, annotationTrack = "grid")
chordDiagram(mat, grid.col = grid.col, annotationTrack = c("name", "grid"),
annotationTrackHeight = c(0.03, 0.01))
chordDiagram(mat, grid.col = grid.col, annotationTrack = NULL)
在绘制和弦图之前,可以分配几个空轨道。然后自定义图形可以添加到这些空轨道之后。预分配轨道的数量可以通过 preAllocateTracks
设置:
chordDiagram(mat, preAllocateTracks = 2)
circos.info()
## All your sectors:
## [1] "S1" "S2" "S3" "E1" "E2" "E3" "E4" "E5" "E6"
##
## All your tracks:
## [1] 1 2 3 4
##
## Your current sector.index is E6
## Your current track.index is 4
预分配轨道的默认设置是:
list(ylim = c(0, 1),
track.height = circos.par("track.height"),
bg.col = NA,
bg.border = NA,
bg.lty = par("lty"),
bg.lwd = par("lwd"))
通过将 preAllocateTracks
指定为一个列表,可以覆盖预分配轨道的默认设置:
chordDiagram(mat, annotationTrack = NULL,
preAllocateTracks = list(track.height = 0.3))
如果需要预分配多个轨道,只需将 preAllocateTracks
指定为包含每个轨道设置的列表:
chordDiagram(mat, annotationTrack = NULL,
preAllocateTracks = list(list(track.height = 0.1),
list(bg.border = "black")))
默认情况下,chordDiagram()
为扇区标签和轴的定制提供了较差的支持,但是使用 preAllocateTracks 可以很容易地定制它们。
2、自定义扇区标签
在chordDiagram()
中,没有参数来控制扇区标签的样式,但这可以通过先预先分配一个空轨道,然后在稍后定制其中的标签来实现。在下面的示例中,首先分配一个轨道,然后添加一个和弦图,没有标签轨道和轴。然后,第一个轨道被更新来添加顺时针方向的标签:
chordDiagram(mat, grid.col = grid.col, annotationTrack = "grid",
preAllocateTracks = list(track.height = max(strwidth(unlist(dimnames(mat))))))
# we go back to the first track and customize sector labels
circos.track(track.index = 1, panel.fun = function(x, y) {
circos.text(CELL_META$xcenter, CELL_META$ylim[1], CELL_META$sector.index,
facing = "clockwise", niceFacing = TRUE, adj = c(0, 0.5))
}, bg.border = NA) # here set bg.border to NA is important
注意circos.text()
和get.cell.meta.data()
可以在 panel.fun 外使用,如果明确指定扇区索引和轨道索引:
chordDiagram(mat, grid.col = grid.col,
annotationTrack = c("grid", "axis"), annotationTrackHeight = mm_h(5))
for(si in get.all.sector.index()) {
xlim = get.cell.meta.data("xlim", sector.index = si, track.index = 1)
ylim = get.cell.meta.data("ylim", sector.index = si, track.index = 1)
circos.text(mean(xlim), mean(ylim), si, sector.index = si, track.index = 1,
facing = "bending.inside", niceFacing = TRUE, col = "white")
}
当扇区宽度小于 10 度时,标签添加道其垂直方向:
set.seed(123)
mat2 = matrix(rnorm(100), 10)
chordDiagram(mat2, annotationTrack = "grid",
preAllocateTracks = list(track.height = max(strwidth(unlist(dimnames(mat))))))
circos.track(track.index = 1, panel.fun = function(x, y) {
xlim = get.cell.meta.data("xlim")
xplot = get.cell.meta.data("xplot")
ylim = get.cell.meta.data("ylim")
sector.name = get.cell.meta.data("sector.index")
if(abs(xplot[2] - xplot[1]) < 10) {
circos.text(mean(xlim), ylim[1], sector.name, facing = "clockwise",
niceFacing = TRUE, adj = c(0, 0.5), col = "red")
} else {
circos.text(mean(xlim), ylim[1], sector.name, facing = "inside",
niceFacing = TRUE, adj = c(0.5, 0), col= "blue")
}
}, bg.border = NA)
3、自定义扇区坐标轴
默认情况下,chordDiagram()在网格轨道上添加轴。但是使用自定义代码很容易进行自定义。
在下面的示例代码中,我们绘制另一种类型的轴,它显示扇区的相对百分比。我们首先通过 preAllocateTracks 预分配一个空轨道,然后回到这个轨道上添加轴。
你可以看到我们将第一个轴添加到第二个轨道的顶部。你也可以把它们放在第一个轨道的底部。
# similar as the previous example, but we only plot the grid track
chordDiagram(mat, grid.col = grid.col, annotationTrack = "grid",
preAllocateTracks = list(track.height = mm_h(5)))
for(si in get.all.sector.index()) {
circos.axis(h = "top", labels.cex = 0.3, sector.index = si, track.index = 2)
}
现在我们回到第一个轨道,添加第二种类型的轴和扇区名称。panel.fun 里如果扇形小于 30 度,轴的刻度设置为 0.5:
# the second axis as well as the sector labels are added in this track
circos.track(track.index = 1, panel.fun = function(x, y) {
xlim = get.cell.meta.data("xlim")
ylim = get.cell.meta.data("ylim")
sector.name = get.cell.meta.data("sector.index")
xplot = get.cell.meta.data("xplot")
circos.lines(xlim, c(mean(ylim), mean(ylim)), lty = 3) # dotted line
by = ifelse(abs(xplot[2] - xplot[1]) > 30, 0.2, 0.5)
for(p in seq(by, 1, by = by)) {
circos.text(p*(xlim[2] - xlim[1]) + xlim[1], mean(ylim) + 0.1,
paste0(p*100, "%"), cex = 0.3, adj = c(0.5, 0), niceFacing = TRUE)
}
circos.text(mean(xlim), 1, sector.name, niceFacing = TRUE, adj = c(0.5, 0))
}, bg.border = NA)
circos.clear()
4、水平或垂直对称
在和弦图中,当有两组时(如果输入是邻近矩阵,则对应行和列),将图旋转到水平方向或垂直方向对称总是比较很美观的:
par(mfrow = c(1, 2))
circos.par(start.degree = 0)
chordDiagram(mat, grid.col = grid.col, big.gap = 20)
abline(h = 0, lty = 2, col = "#00000080")
circos.clear()
circos.par(start.degree = 90)
chordDiagram(mat, grid.col = grid.col, big.gap = 20)
abline(v = 0, lty = 2, col = "#00000080")
circos.clear()
5、比较两个和弦图
通常,在和弦图中,mat 中的值被归一化为矩阵中绝对值的总和,这意味着连接的宽度仅代表相对值。然后,当比较两个和弦图时,有必要将两个图中连接的单位宽度用相同的比例表示。这个问题可以通过在具有较小矩阵的 Chord 图中添加较大的间隙来解决。
首先我们做一个大的和弦图:
mat1 = matrix(sample(20, 25, replace = TRUE), 5)
chordDiagram(mat1, directional = 1, grid.col = rep(1:5, 2), transparency = 0.5,
big.gap = 10, small.gap = 1) # 10 and 1 are default values for the two arguments
第二个矩阵的值只有 mat1 的一半:
mat2 = mat1 / 2
calc gap()
可以用来计算第二个和弦图的间隙,使两个和弦图的比例相同:
gap = calc_gap(mat1, mat2, big.gap = 10, small.gap = 1)
chordDiagram(mat2, directional = 1, grid.col = rep(1:5, 2), transparency = 0.5,
big.gap = gap, small.gap = 1)
现在,如果你比较两个图中的轴的比例,两个和弦的比例是相同的:
6、多组和弦图
首先生成一个随机矩阵,其中有三组(A, B, C)。注意,这个功能对于输入数据框来说是一样的:
mat1 = matrix(rnorm(25), nrow = 5)
rownames(mat1) = paste0("A", 1:5)
colnames(mat1) = paste0("B", 1:5)
mat2 = matrix(rnorm(25), nrow = 5)
rownames(mat2) = paste0("A", 1:5)
colnames(mat2) = paste0("C", 1:5)
mat3 = matrix(rnorm(25), nrow = 5)
rownames(mat3) = paste0("B", 1:5)
colnames(mat3) = paste0("C", 1:5)
mat = matrix(0, nrow = 10, ncol = 10)
rownames(mat) = c(rownames(mat2), rownames(mat3))
colnames(mat) = c(colnames(mat1), colnames(mat2))
mat[rownames(mat1), colnames(mat1)] = mat1
mat[rownames(mat2), colnames(mat2)] = mat2
mat[rownames(mat3), colnames(mat3)] = mat3
mat
## B1 B2 B3 B4 B5 C1
## A1 0.9769734 3.2410399 0.51686204 2.1284519 0.4365235 -0.86551286
## A2 -0.3745809 -0.4168576 0.36896453 -0.7413361 -0.4583653 -0.23627957
## A3 1.0527115 0.2982276 -0.21538051 -1.0959963 -1.0633261 -0.19717589
## A4 -1.0491770 0.6365697 0.06529303 0.0377884 1.2631852 1.10992029
## A5 -1.2601552 -0.4837806 -0.03406725 0.3104807 -0.3496504 0.08473729
## B1 0.0000000 0.0000000 0.00000000 0.0000000 0.0000000 1.65090747
## B2 0.0000000 0.0000000 0.00000000 0.0000000 0.0000000 -0.05402813
## B3 0.0000000 0.0000000 0.00000000 0.0000000 0.0000000 0.11924524
## B4 0.0000000 0.0000000 0.00000000 0.0000000 0.0000000 0.24368743
## B5 0.0000000 0.0000000 0.00000000 0.0000000 0.0000000 1.23247588
## C2 C3 C4 C5
## A1 0.75405379 -0.8953634 -0.61116592 0.54319406
## A2 -0.49929202 -1.3108015 -1.18548008 -0.41433995
## A3 0.21444531 1.9972134 2.19881035 -0.47624689
## A4 -0.32468591 0.6007088 1.31241298 -0.78860284
## A5 0.09458353 -1.2512714 -0.26514506 -0.59461727
## B1 -0.51606383 -1.2362731 0.70758835 0.88465050
## B2 -0.99250715 -1.2847157 -0.36365730 -1.01559258
## B3 1.67569693 -0.5739735 0.05974994 1.95529397
## B4 -0.44116322 0.6179858 -0.70459646 -0.09031959
## B5 -0.72306597 1.1098481 -0.71721816 0.21453883
设置分组变量:
nm = unique(unlist(dimnames(mat)))
group = structure(gsub("\\d", "", nm), names = nm)
group
## A1 A2 A3 A4 A5 B1 B2 B3 B4 B5 C1 C2 C3 C4 C5
## "A" "A" "A" "A" "A" "B" "B" "B" "B" "B" "C" "C" "C" "C" "C"
将 group 变量赋给 group 参数:
grid.col = structure(c(rep(2, 5), rep(3, 5), rep(4, 5)),
names = c(paste0("A", 1:5), paste0("B", 1:5), paste0("C", 1:5)))
chordDiagram(mat, group = group, grid.col = grid.col)
circos.clear()
换一种分组:
group = structure(gsub("^\\w", "", nm), names = nm)
group
## A1 A2 A3 A4 A5 B1 B2 B3 B4 B5 C1 C2 C3 C4 C5
## "1" "2" "3" "4" "5" "1" "2" "3" "4" "5" "1" "2" "3" "4" "5"
chordDiagram(mat, group = group, grid.col = grid.col)
circos.clear()
分组的顺序控制扇区的顺序,如果将分组设置为一个因子,则 levels 的顺序控制分组的顺序:
group = structure(gsub("\\d", "", nm), names = nm)
group = factor(group[sample(length(group), length(group))], levels = c("C", "A", "B"))
group
## B3 A3 B5 B2 C1 A1 A2 C4 A5 C2 C5 B4 A4 B1 C3
## B A B B C A A C A C C B A B C
## Levels: C A B
chordDiagram(mat, group = group, grid.col = grid.col)
circos.clear()
big.gap 参数控制组内之间的扇区间隔,small.gap 参数控制扇区之间的间隔:
group = structure(gsub("\\d", "", nm), names = nm)
chordDiagram(mat, group = group, grid.col = grid.col, big.gap = 20, small.gap = 5)
circos.clear()
标签和其他轨道可以手动调整:
group = structure(gsub("\\d", "", nm), names = nm)
chordDiagram(mat, group = group, grid.col = grid.col,
annotationTrack = c("grid", "axis"),
preAllocateTracks = list(
track.height = mm_h(4),
track.margin = c(mm_h(4), 0)
))
circos.track(track.index = 2, panel.fun = function(x, y) {
sector.index = get.cell.meta.data("sector.index")
xlim = get.cell.meta.data("xlim")
ylim = get.cell.meta.data("ylim")
circos.text(mean(xlim), mean(ylim), sector.index, cex = 0.6, niceFacing = TRUE)
}, bg.border = NA)
highlight.sector(rownames(mat1), track.index = 1, col = "red",
text = "A", cex = 0.8, text.col = "white", niceFacing = TRUE)
highlight.sector(colnames(mat1), track.index = 1, col = "green",
text = "B", cex = 0.8, text.col = "white", niceFacing = TRUE)
highlight.sector(colnames(mat2), track.index = 1, col = "blue",
text = "C", cex = 0.8, text.col = "white", niceFacing = TRUE)
circos.clear()
下面的代码可以在不使用 group 参数的情况下实现前面的示例,但是需要手动计算 gap.after
的正确值。设置 group 参数会自动为您完成:
circos.par(gap.after = c(rep(1, 4), 5, rep(1, 4), 5, rep(1, 4), 5))
chordDiagram(mat, order = names(sort(group)), grid.col = grid.col)
circos.clear()
7、复杂和弦图
作者还提供了绘制复杂和弦图的一些代码,大家感兴趣可以去看看,这里放一些图展示一下:
END
发现更多精彩
关注公众号
欢迎小伙伴留言评论!
今天的分享就到这里了,敬请期待下一篇!
最后欢迎大家分享转发,您的点赞是对我的鼓励和肯定!
如果觉得对您帮助很大,赏杯快乐水喝喝吧!
推 荐 阅 读