Multiple-group Chord diagram

Zuguang Gu · June 2020 · 版权信息

From verion 0.4.10 of the circlize package, there is a new group argument in chordDiagram() function which is very convenient for making multiple-group Chord diagrams.

I first generate a random matrix where there are three groups (A, B, and C). Note this new functionality works the same for the input as a data frame.

library(circlize)
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     C2    C3     C4     C5
## A1  0.835 -0.63 0.025 -0.61 -1.595  1.86 -0.099  1.15 -1.272 -1.169
## A2  0.048 -0.93 1.194 -0.16 -0.398 -1.18 -1.697  0.94 -0.121  0.603
## A3  0.225 -0.30 0.451  0.70  0.105  0.44  0.787 -0.70 -0.263 -0.079
## A4 -0.185  0.32 1.374  0.66 -0.058 -2.00  0.738 -0.11 -2.979  0.126
## A5 -1.569  0.95 1.858  0.36 -0.694  0.13  0.431 -0.47 -0.221 -0.683
## B1  0.000  0.00 0.000  0.00  0.000  0.51 -1.016  0.48  0.016 -1.180
## B2  0.000  0.00 0.000  0.00  0.000 -0.30  0.713 -1.12  1.171  0.960
## B3  0.000  0.00 0.000  0.00  0.000  0.40 -1.177  1.49 -2.117 -0.641
## B4  0.000  0.00 0.000  0.00  0.000 -0.20  0.666  0.14 -1.145  0.222
## B5  0.000  0.00 0.000  0.00  0.000 -1.11  0.266  0.44 -1.391 -0.787

The main thing is to create “a grouping variable”. The variable contains the group labels and the sector names are used as the names in the vector.

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"

Assign group variable to the group argument:

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()

We can try another grouping:

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()

The order of group controls the sector orders and if group is set as a factor, the order of levels controls the order of groups.

group = structure(gsub("\\d", "", nm), names = nm)
group = factor(group[sample(length(group), length(group))], levels = c("C", "A", "B"))
group
## C4 C3 B2 C5 C2 B4 A4 B3 A3 A1 B1 C1 A2 B5 A5 
##  C  C  B  C  C  B  A  B  A  A  B  C  A  B  A 
## Levels: C A B
chordDiagram(mat, group = group, grid.col = grid.col)
circos.clear()

The gap between groups is controlled by big.gap argument and the gap between sectors is controlled by small.gap argument.

group = structure(gsub("\\d", "", nm), names = nm)
chordDiagram(mat, group = group, grid.col = grid.col, big.gap = 20, small.gap = 5)
circos.clear()

As a normal Chord diagram, the labels and other tracks can be manually adjusted:

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()

本文使用 CC BY-NC-SA 4.0 协议发布。