查看原文
其他

circlize 之 chordDiagram 函数高级用法

JunJunLab 老俊俊的生信笔记 2022-08-15

点击上方关注我们




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 参数控制。此参数的可用值有 gridnameaxis。注释轨道的高度可以通过 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.030.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(01),
     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(00.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(00.5), col = "red")
    } else {
        circos.text(mean(xlim), ylim[1], sector.name, facing = "inside",
            niceFacing = TRUE, adj = c(0.50), 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]) > 300.20.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.50), niceFacing = TRUE)
    }

    circos.text(mean(xlim), 1, sector.name, niceFacing = TRUE, adj = c(0.50))
}, bg.border = NA)
circos.clear()

4、水平或垂直对称


在和弦图中,当有两组时(如果输入是邻近矩阵,则对应行和列),将图旋转到水平方向或垂直方向对称总是比较很美观的:

par(mfrow = c(12))
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(2025, replace = TRUE), 5)
chordDiagram(mat1, directional = 1, grid.col = rep(1:52), 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:52), 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(25), rep(35), rep(45)),
                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(14), 5, rep(14), 5, rep(14), 5))
chordDiagram(mat, order = names(sort(group)), grid.col = grid.col)
circos.clear()

7、复杂和弦图


作者还提供了绘制复杂和弦图的一些代码,大家感兴趣可以去看看,这里放一些图展示一下:




END




所以今天你学习了吗?



发现更多精彩

关注公众号

欢迎小伙伴留言评论!

今天的分享就到这里了,敬请期待下一篇!

最后欢迎大家分享转发,您的点赞是对我的鼓励肯定

如果觉得对您帮助很大,赏杯快乐水喝喝吧!

推 荐 阅 读




您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存