jokergoo / circlize

Circular visualization in R
http://jokergoo.github.io/circlize_book/book/
Other
963 stars 145 forks source link

issue when target rows <2 #252

Closed splaisan closed 3 years ago

splaisan commented 3 years ago

I made a formula to plot pairwise interactions as chordDiagram between two columns and I get an error when I have a unique ID in the second column. In the example column J is always 'J-1' so only one common target for all

my code returns: Error:dfshould have at least have two columns. triggered by the chordDiagram(mat2, annotationTrack = "grid", ...) command in the vis.genus function

Do you have a hint on where I should change my code to accept unique target in a chord diagram?

Thanks in advance

======

A piece of data (one) triggering the error

   chain                              V.name J.name Clones
1:   TRD                                 V-1    J-1      5
2:   TRD                          V-13-4/DV7    J-1      4
3:   TRD  V-14D-2,V-14D-3/DV8,V-14-3,V-14D-1    J-1      2
4:   TRD                        V-15-1/DV6-1    J-1     25
5:   TRD                V-15-1/DV6-1,V-15N-1    J-1      3
6:   TRD V-15-2/DV6-2,V-15N-2,V-15D-2/DV6D-2    J-1      1
7:   TRD                               V-2-2    J-1     23
8:   TRD                           V-21/DV12    J-1      2
chain.clones <- sum(one$Clones)
percent <- round(100*chain.clones/group.clones,2)
title <- paste0(one.chain, ": ", group, " [n=", chain.clones, " of ", totclon, ", ", percent, "%]", sep="")
filename <- paste0(group, "_", one.chain, ".png", sep="")
circos.clear()
# add space around for text
circos.par("canvas.xlim" = c(-1.5, 1.5), "canvas.ylim" = c(-1.5, 1.5))
png(paste0("circos_plots/", group, "/", filename, sep=""), width = 600, height = 600)
suppressWarnings(vis_genus(one, cex=1, cex.main=1.5, title=title, label.dist=-0.5))
forget <- dev.off()

the code uses this formula (adapted from the immdata code)

vis_genus <- function(x, title="", cex=1, cex.main=1.5, label.dist=-0.25, ...) {

  # input is a single sample 'data' from a immunarch import  

  # example run
  # data(immdata)
  # mydat <- immdata$data$`A2-i129`
  # circos.clear()
  # circos.par("canvas.xlim" = c(-1.5, 1.5), "canvas.ylim" = c(-1.5, 1.5))
  # vis_genus(mydat, title="A2-i129_sample")

  require("stringr", "circlize")

  # extract unique V and J genes
  V <- sort(unique(unlist(strsplit(paste(x$V.name, collapse=","), ","))))
  J <- sort(unique(unlist(strsplit(paste(x$J.name, collapse=","), ","))))

  # create and fill matrix of pairwise with sum(Clones)
  mat <- matrix(nrow = length(J), ncol = length(V))
  colnames(mat) <- V
  rownames(mat) <- J
  for (j in seq(1:length(J))) {
    for (v in seq(1:length(V))) {
      sub <- x[(str_detect(x$V.name, V[[v]], negate = FALSE) & 
                  str_detect(x$J.name, J[[j]], negate = FALSE)),]
      mat[j,v] <- sum(sub$Clones)
    }
  }

  # convert to proportions / fraction of total
  mat <- mat/sum(mat)

  # reorder by V decreasing and J increasing order
  vmax <- colSums(mat)
  jmax <- rowSums(mat) 
  mat2 <- mat[order(jmax, decreasing = FALSE), order(vmax, decreasing = TRUE)]

  # create circos plot
  chordDiagram(mat2, annotationTrack = "grid", ...)

  # add legends
  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(label.dist, 0.5),
                cex = cex)
  }, bg.border = NA)

  # add title
  title(title, cex.main=cex.main)
}
splaisan commented 3 years ago

Fixed!

The error was mine as always ;-) I created matrices with only one row and forgot to add 'drop=FALSE' in the order command, leading to loosing one dimention.

After correcting another error of mine in the V and J definition (unexpected partial matches), the final new fonction does the job beautifully; I add it here only for reference

Thanks a lot for the beautiful toolbox!

vis_genus2 <- function(x, title="", cex=1, cex.main=1.5, label.dist=-0.25, ...) {

  # input is a single sample 'data' from a immunarch import  

  # example run
  # data(immdata)
  # mydat <- immdata$data$`A2-i129`
  # circos.clear()
  # circos.par("canvas.xlim" = c(-1.5, 1.5), "canvas.ylim" = c(-1.5, 1.5))
  # vis_genus(mydat, title="A2-i129_sample")

  require("stringr", "circlize")

  # replace V.name lists by first element with star
  x$V.name <- gsub(",.*", "*", x$V.name)
  x$J.name <- gsub(",.*", "*", x$J.name)

  # extract unique V and J genes
  V <- sort(unique(unlist(strsplit(paste(x$V.name, collapse=","), ","))))
  J <- sort(unique(unlist(strsplit(paste(x$J.name, collapse=","), ","))))

  # create and fill matrix of pairwise with sum(Clones)
  mat <- matrix(nrow = length(J), ncol = length(V))
  colnames(mat) <- V
  rownames(mat) <- J

  # sum up counts for identical V+J clonotype pairs
  for (j in seq(1:length(J))) {
    for (v in seq(1:length(V))) {
      sub <- x[x$V.name==V[[v]] & x$J.name==J[[j]],'Clones']
      mat[j,v] <- sum(sub$Clones)
    }
  }

  # convert to proportions / fraction of total
  mat <- mat/sum(mat)

  # reorder by V decreasing and J increasing order
  vmax <- colSums(mat)
  jmax <- rowSums(mat) 
  mat2 <- mat[order(jmax, decreasing = FALSE), order(vmax, decreasing = TRUE), drop=FALSE]

  # create circos plot
  chordDiagram(mat2, annotationTrack = "grid", ...)

  # add legends
  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(label.dist, 0.5),
                cex = cex)
  }, bg.border = NA)

  # add title
  title(title, cex.main=cex.main)
}