rcirclize

Big and small gaps using circlize in R


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. enter image description here


Solution

  • 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.