R Circlize "Detect some gaps are too large"
Asked Answered
C

2

5

I would like make a similar graph posted here using our data, however, I received this error message “Detect some gaps are too large”. Do you think because some values are very small as compare to others (e.g.; 1 versus 1812)?. I made a few changes to my data as in matrix 2 by adding a few zeros after a 1 or 2 and it works. Is there any way to work around with this range of data? I would like to plot this beautiful graph using my real data (matrix 1). Any help is much appreciated.

library(circlize)
#matrix 1
#level0 <- c(1, 8, 39, 14, 2)
#level1 <- c(1, 19, 153, 93, 1)
#level2 <- c(2, 19, 274, 46, 13)
#level3 <- c(0, 8, 152, 1812, 465)
#level4 <- c(0, 2, 1, 164, 226)

#matrix 2
#level0 <- c(100,8,39,14,200)
#level1 <- c(100,190, 153,93,100)
#level2 <- c(200,19,274,646,130)
#level3 <- c(200,800,152,1812,465)
#level4 <- c(200,200,100,164,226)

#build matrix 2
a <- list(c(100,8,39,14,200),c(100,19, 153,93,100), c(200,19,274,646,13),    c(200,8,152,1812,465),c(200,200,100,164,226))
mat <- do.call(rbind, a)
#mat = matrix(sample(1:100, 25, replace = TRUE), 5, 5)
rownames(mat) = c("level 0", "level 1", "level 2", "level 3", "level 4")
colnames(mat) = c("Level0", "Level1", "Level2", "Level3", "Level4")
rn = rownames(mat)
cn = colnames(mat)

factors = c(rn, rev(cn))
factors = factor(factors, levels = factors)
col_sum = apply(mat, 2, sum)
row_sum = apply(mat, 1, sum)
xlim = cbind(rep(0, 10), c(row_sum, col_sum))

par(mar = c(1, 1, 1, 1))
circos.par(cell.padding = c(0, 0, 0, 0), clock.wise = FALSE, track.margin=c(0,0.1),
           gap.degree = 4, start.degree =90)
circos.initialize(factors = factors, xlim = xlim
                  , sector.width = c(row_sum/sum(row_sum), col_sum/sum(col_sum)))
circos.trackPlotRegion(factors = factors, ylim = c(0, 1), bg.border = NA,
                       # bg.col = c("red", "orange", "yellow", "green", "blue", rep("grey", 5)), track.height = 0.05,
                       bg.col = c(c("red", "orange", "yellow", "green", "blue"),
                                  c("blue", "green", "yellow", "orange", "red")), track.height = 0.05,
                       panel.fun = function(x, y) {
                         sector.name = get.cell.meta.data("sector.index")
                         xlim = get.cell.meta.data("xlim")
                         circos.text(mean(xlim), 3, sector.name, adj = c(0.5, 0))
                         circos.axis(labels.cex=0.8, direction="outside", labels.away.percentage=0.5)
                         if(sector.name %in% rn) {
                           for(i in seq_len(ncol(mat))) {
                             circos.lines(rep(sum(mat[sector.name, seq_len(i)]), 2), c(0, 1),
                                          col = "white")
                           }
                         } else if(sector.name %in% cn) {
                           for(i in seq_len(nrow(mat))) {
                             circos.lines(rep(sum(mat[ seq_len(i), sector.name]), 2), c(0, 1),
                                          col = "white")
                           }
                         }
                       })
col = c("#FF000020", "#FFA50020", "#FFFF0020", "#00FF0020", "#0000FF20")
for(i in seq_len(nrow(mat))) {
  for(j in seq_len(ncol(mat))) {
    circos.link(rn[i], c(sum(mat[i, seq_len(j-1)]), sum(mat[i, seq_len(j)])),
                cn[j], c(sum(mat[seq_len(i-1), j]), sum(mat[seq_len(i), j])),
                col = col[i], border = "white")
  }
}
Cipango answered 28/5, 2014 at 15:55 Comment(1)
you can also get a plot from your code if change gap.degree=0.1Outwork
O
8

So I think your df1 object is a little different to my original code. If you set up the matrix m and df1 as such...

m <- matrix(c(1, 8, 39, 14, 2, 
              1, 19, 153, 93, 1,
              2, 19, 274, 46, 13,
              0, 8, 152, 1812, 465,
              0, 2, 1, 164, 226), nrow=5, byrow=TRUE)
df1 <- data.frame(order=1:5, region=paste0("level",1:5), 
              rcol = c("red", "orange", "yellow", "green", "blue"),
              lcol = c("#FF000020", "#FFA50020", "#FFFF0020", "#00FF0020", "#0000FF20"),
              stringsAsFactors=FALSE)
df1$region <- factor(df1$region, levels=df1$region)
df1$xmin <- 0
df1$xmax <- rowSums(m)+colSums(m)
n <-nrow(df1)

dimnames(m) <- list(orig=df1$region,dest=df1$region)

You get the following objects...

> df1
  order region   rcol      lcol xmin xmax
1     1 level1    red #FF000020    0   68
2     2 level2 orange #FFA50020    0  323
3     3 level3 yellow #FFFF0020    0  973
4     4 level4  green #00FF0020    0 4566
5     5 level5   blue #0000FF20    0 1100
> addmargins(m)
        dest
orig     level1 level2 level3 level4 level5  Sum
  level1      1      8     39     14      2   64
  level2      1     19    153     93      1  267
  level3      2     19    274     46     13  354
  level4      0      8    152   1812    465 2437
  level5      0      2      1    164    226  393
  Sum         4     56    619   2129    707 3515

I explain in more detail the purpose of df1 in the working paper. Briefly, the df1 object contains information on the lengths of sectors to be plotted (xmin and xmax) and the colours for the circle rectangles on the outside rcol and ribbon link colours lcol. You could of course have the same lcol and rcol,...adapt away until you get a palette/style that you like (perhaps a little less transparency for the lcol).

You can then go ahead and use very similar code as to what I have in the demo file in the migest package to get a plot (I only changed the circos.axis axis arguments and the subset of df2)...

library(circlize)
library(plyr)
par(mar=rep(0,4))
circos.clear()

#basic circos graphic parameters
circos.par(cell.padding=c(0,0,0,0), track.margin=c(0,0.15), start.degree = 90, gap.degree =4)

#sector details
circos.initialize(factors = df1$region, xlim = cbind(df1$xmin, df1$xmax))

#plot sectors
circos.trackPlotRegion(ylim = c(0, 1), factors = df1$region, track.height=0.1,
 #panel.fun for each sector
 panel.fun = function(x, y) {
   #select details of current sector
   name = get.cell.meta.data("sector.index")
   i = get.cell.meta.data("sector.numeric.index")
   xlim = get.cell.meta.data("xlim")
   ylim = get.cell.meta.data("ylim")

   #plot labels
   circos.text(x=mean(xlim), y=2.2, labels=name, facing = "arc", cex=0.8)

   #plot main sector
   circos.rect(xleft=xlim[1], ybottom=ylim[1], xright=xlim[2], ytop=ylim[2], col = df1$rcol[i], border=df1$rcol[i])

   #blank in part of main sector
   #circos.rect(xleft=xlim[1], ybottom=ylim[1], xright=xlim[2]-rowSums(m)[i], ytop=ylim[1]+0.3, col = "white", border = "white")

   #white line all the way around
   #circos.rect(xleft=xlim[1], ybottom=0.3, xright=xlim[2], ytop=0.32, col = "white", border = "white")

   #plot axis
   circos.axis(labels.cex=0.6, major.at=seq(from=0,to=floor(df1$xmax)[i],by=500), 
                                 labels.away.percentage = 0.15)
})

##
##plot links
##
#add sum values to df1, marking the x-position of the first links out (sum1) and in (sum2). Updated for further links in loop below.
df1$sum1 <- colSums(m)
df1$sum2 <- numeric(n)

#create a data.frame of matrix sorted by element size, to allow largest plotted first
df2 <- cbind(as.data.frame(m),orig=rownames(m),  stringsAsFactors=FALSE)
df2 <- reshape(df2, idvar="orig", varying=list(1:n), direction="long", timevar="dest", time=rownames(m),  v.names = "m")
df2 <- arrange(df2,desc(m))

#loose non zero links
df2 <- subset(df2, m>0)

#plot links
for(k in 1:nrow(df2)){
  #i,j reference of flow matrix
  i<-match(df2$orig[k],df1$region)
  j<-match(df2$dest[k],df1$region)

  #plot link
  circos.link(sector.index1=df1$region[i], point1=c(df1$sum1[i], df1$sum1[i] + abs(m[i, j])),
          sector.index2=df1$region[j], point2=c(df1$sum2[j], df1$sum2[j] + abs(m[i, j])),
          col = df1$lcol[i])

  #update sum1 and sum2 for use when plotting the next link
  df1$sum1[i] = df1$sum1[i] + abs(m[i, j])
  df1$sum2[j] = df1$sum2[j] + abs(m[i, j])
}

Which gives a plot like this...

enter image description here

If you want to add some directionality to the plot then uncomment the two lines in panel.fun which add the white rectangles and boarder line.

Outwork answered 28/5, 2014 at 16:57 Comment(7)
I actually wanted a one direction flow, and it does not allow the same name for both row and column. I wanted to reproduce the above graph too, but it does work for me. I copied and pasted your codes and somehow it's not the same. Did you change other parameters besides the circos.asis and df2. Thanks for all your help Guy.Cipango
I think it looks better with your graph. Instead of drawing ribbons of the same levels across the circos, we can plot them within each level.Cipango
@Cipango My bad, I had a argument top rather than ytop in the first circos.rect argument. Should work now? I also commented out the white rectangles which overlapped the coloured rectangles...to get rid of the directionality.Outwork
This is great. I just reproduced your graph and it looks awesome. Thanks again for your help.Cipango
@gjabel - Thanks for this. Quick points.... it seems that for circos.text the argument 'direction' is depreciated - have to use 'facing' instead. Also, I tried running this code in it's entirety and got the following error: Error in circos.link(sector.index1 = df1$region[i], point1 = c(df1$sum1[i], : unused arguments (top.ratio = 0.66, top.ratio.low = 0.67) - is this also something that has changed with the updated circlize version? or is the error only for me?Spank
@japapic Thanks for the spot. The error you got is also from the new update (top.ratio and top.ratio.low seem to have gone, replaced by h and h2). Now, by default the links look very similar to those in the plot above, so, at least in the case above, there is no need to provide an h and h2. I have adjusted the code in the answer to match to the updated circlize package. Should run error free now.Outwork
@gjabel thanks - one extra thing. Apparently the 'facing' cannot take 'arc' as an option. I get an error message like this: 'arg' should be one of “inside”, “outside”, “reverse.clockwise”, “clockwise”, “downward”, “bending” ..... using any one of these works though.Spank
C
1

Great! Here what I did to get the following graph: - changed gap.degree=0.1 - converted the numbers to percentage (mat<-mat/4700*100) - remove the rev() in the factors

enter image description here

Cipango answered 28/5, 2014 at 19:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.