How do I group categories so that they appear with big gaps in a chord diagram generated using the circlize package in R?
For example, given the following adjacency matrix:
A B C D E F G H
A 0 0 0 0 0 0 1168 0
B 0 0 2545 278 0 0 0 337
C 0 0 0 817 0 0 0 0
D 0 0 0 0 10 0 0 0
E 0 0 0 0 0 0 0 0
F 0 0 0 0 0 0 0 0
G 0 0 561 326 0 0 0 281
H 0 0 46 8 0 0 0 0
I would like to create the three groups {A}, {B,C}, and {D,E,F,G,H}, so that when using chordDiagram()
its parameter small.gap
is used between segments within a group and big.gap
is used between groups.
Note that this is code that will be run daily in production, and I cannot guarantee that movements always will occur between all categories. In the example above, no movements occur from or to the category F, resulting in it being omitted from the output. Using gap.after
I hard-coded the desired result (see image), but this is not a feasible solution since I do not know which categories will be drawn or not. I would also prefer that a solution is not dependent on the ordering of the columns and rows in the matrix.
In the upcoming version 0.4.10, circlize will support a named vector as input for gap.after
. It is already in the master branch, so after pulling it in R it is possible to programmatically produce the correct gaps.
library(devtools)
install_github("jokergoo/circlize")
library(circlize) # chord diagrams
mat = read.table(textConnection("
A B C D E F G H
A 0 0 0 0 0 0 1168 0
B 0 0 2545 278 0 0 0 337
C 0 0 0 817 0 0 0 0
D 0 0 0 0 10 0 0 0
E 0 0 0 0 0 0 0 0
F 0 0 0 0 0 0 0 0
G 0 0 561 326 0 0 0 281
H 0 0 46 8 0 0 0 0"))
mat = as.matrix(mat)
big_gap = 10
small_gap = 2
# For example three groups x,y,z
sector_grouping = cbind.data.frame(
"Sec" = c("A","B","C","D","E","F","G","H"),
"Grp" = c("x","y","y","z","z","z","z","z")
)
# Check which, if any, groups lack both outgoing and incoming
empty_row = rowSums(mat)[sector_grouping$Sec] == 0
empty_column = colSums(mat)[sector_grouping$Sec] == 0
# Remove empty sectors
sector_grouping = sector_grouping[!(empty_row & empty_column),]
# Iterate and set a big gap (last sector in group) or small gap.
for(i in 1:nrow(sector_grouping)) {
sector_grouping[i,"Gap"] = ifelse(
sector_grouping[i,2]==sector_grouping[i+1,2],
small_gap,
big_gap
)
}
# The last sector needs fixing (always assumed big)
sector_grouping$Gap[is.na(sector_grouping$Gap)] = big_gap
# Build named vector
gap.after = sector_grouping$Gap
names(gap.after) = sector_grouping$Sec
circos.par(gap.after = gap.after)
chordDiagram(mat, order = LETTERS[1:8])
circos.clear()
I'd like to thank the author @Zuguang Gu of circlize for the prompt help after I reached out to him.