Drawing nested venn diagrams

空扰寡人 提交于 2019-12-21 12:49:12

问题


I have data which contain binary indicators for two groups, and to more groups that are nested within one of the first two groups.

For example:

set.seed(1)
df <- data.frame(a=rep(0,10),b=rep(0,10),b.1=rep(0,10),b.2=rep(0,10))
df$a[sample(10,5,replace=F)] <- 1
df$b[sample(10,5,replace=F)] <- 1
df$b.1[sample(which(df$b==1),3,replace=F)] <- 1
df$b.2[sample(which(df$b==1),3,replace=F)] <- 1
df <- df[which(rowSums(df)==0),]

a and b are the two groups and b.1 and b.2 are nested within group b.

What I'd like to do is draw one venn diagram of all groups. This means that b.1 and b.2 will be circumscribed within b, which will intersect a.

Is there any way to achieve this? Using a ggplot solution would be great.

Trying R's VennDiagram' only for groups b, b.1, and b.2 doesn't even work for me:

library(VennDiagram)
draw.triple.venn(area1=sum(df$b),area2=sum(df$b.1),area3=sum(df$b.2),
                   n12=sum(df$b*df$b.1),n23=sum(df$b.1*df$b.2),n13=sum(df$b*df$b.2),n123=sum(df$b*df$b.1*df$b.2),
                   category=c("b","b1","b2"))

With the Vennerable package I get close only drawing the "b" groups:

library(Vennerable)
plot(Venn(Sets=list(b=which(df$b==1),b.1=which(df$b.1==1),b.2=which(df$b.2==1))),doEuler=T,doWeight=T)

But when I add the a group it gets messed up:

Because what I really need is one circle for group a with an intersecting area with group b, and within the circle of group b are the circles of groups b.1 and b.2.


回答1:


The main idea is to draw a triple Venn with a, b1, and b2, and then manually overlay an ellipse for b.

library(VennDiagram)
library(gridExtra)
polygons <- draw.triple.venn(
    area1=sum(df$a),
    area2=sum(df$b.1),
    area3=sum(df$b.2),
    n12=sum(df$a*df$b.1),
    n23=sum(df$b.1*df$b.2),
    n13=sum(df$a*df$b.2),
    n123=sum(df$a*df$b.1*df$b.2),
    category=c("a","b1","b2"),
    margin=.1)

Now we draw the ellipse and add the label. This requires a fair bit of trial and error to get the location, angle, and size right. As it is, it's not perfect, but it's almost there.

b <- ellipseGrob(
    x=unit(0.562,"npc"),
    y=unit(0.515,"npc"),
    angle=(1.996*pi)/3,
    size=65.5, ar=2, gp=gpar(lwd=2.2))
grid.draw(b)
grid.text("b", x=unit(.9,"npc"), y=unit(.9,"npc"), gp=gpar(fontfamily="serif"))




回答2:


In your assumption, there are few patterns of circle locations. I think it would be better to make your function().

Here is my example (edited; change default vp):

nest_venn <- function(data_list, fill = c(2, 4, 5, 6), alpha = 0.15, 
                      vp = viewport(height=unit(1 ,"snpc"), width=unit(1,"snpc"))) {
  counts <- get.venn.partitions(data_list)$..count..      # calculation of each area's value
  if(any(counts[c(3, 4, 7, 8, 11, 12)]==!0)) warning("data_list[[3]] and/or data_list[[4]] isn't nested")
  grobs <- grobTree(
    circleGrob(x = 0.33, y = 0.5, r = 0.3, gp = gpar(fill = alpha(fill[1], alpha), col=8, lwd = 2)),  # a circle
    circleGrob(x = 0.67, y = 0.5, r = 0.3, gp = gpar(fill = alpha(fill[2], alpha), col=8, lwd = 2)),  # b circle
    circleGrob(x = 0.67, y = 0.6, r = 0.16, gp = gpar(fill = alpha(fill[3], alpha), col=8, lwd = 2)), # b.1 circle
    circleGrob(x = 0.67, y = 0.4, r = 0.16, gp = gpar(fill = alpha(fill[4], alpha), col=8, lwd = 2)), # b.2 circle
    textGrob(names(data_list)[1], x = 0.33, y = 0.82, gp = gpar(cex = 1, fontface = 4)), # a label
    textGrob(names(data_list)[2], x = 0.67, y = 0.82, gp = gpar(cex = 1, fontface = 4)), # b label
    textGrob(names(data_list)[3], x = 0.83, y = 0.7, gp = gpar(cex = 1, fontface = 4)),  # b.1 label
    textGrob(names(data_list)[4], x = 0.83, y = 0.3, gp = gpar(cex = 1, fontface = 4)),  # b.2 label
    textGrob(counts[15], x = 0.28, y = 0.5, gp = gpar(cex = 1.2)),  # a
    textGrob(counts[14], x = 0.9, y = 0.5, gp = gpar(cex = 1.2)),   #     b
    textGrob(counts[13], x = 0.47, y = 0.5, gp = gpar(cex = 1.2)),  # a & b
    textGrob(counts[10], x = 0.68, y = 0.65, gp = gpar(cex = 1.2)), #     b & b.1
    textGrob(counts[6], x = 0.68, y = 0.35, gp = gpar(cex = 1.2)),  #     b       & b.2
    textGrob(counts[9], x = 0.57, y = 0.6, gp = gpar(cex = 1.2)),   # a & b & b.1
    textGrob(counts[5], x = 0.57, y = 0.4, gp = gpar(cex = 1.2)),   # a & b       & b.2
    textGrob(counts[2], x = 0.69, y = 0.5, gp = gpar(cex = 1.2)),   #     b & b.1 & b.2
    textGrob(counts[1], x = 0.6, y = 0.5, gp = gpar(cex = 1.2)),    # a & b & b.1 & b.2
    vp = vp)
  return(grobs)
}

preparation of data list:

set.seed(1)
df <- data.frame(a=rep(0,10),b=rep(0,10),b.1=rep(0,10),b.2=rep(0,10))
df$a[sample(10,5,replace=F)] <- 1
df$b[sample(10,5,replace=F)] <- 1
df$b.1[sample(which(df$b==1),3,replace=F)] <- 1
df$b.2[sample(which(df$b==1),3,replace=F)] <- 1
df <- df[-which(rowSums(df)==0),]            # the same as OP's example data

data_list <- list()
for(i in colnames(df)) data_list[[i]] <- which(df[,i]==1)
  # > data_list[1]
  # $a
  # [1] 2 3 4 5 7

use above function and draw the output:

library(VennDiagram); library(grid); library(ggplot2)

nestvenn.obj <- nest_venn(data_list)
grid.newpage()
grid.draw(nestvenn.obj)

# [ edited ]
# If you want a fixed size etc, please give an argument, vp.
vp1 <- viewport(height=unit(150 ,"mm"), width=unit(150, "mm")) # example
nestvenn.obj <- nest_venn(data_list, vp = vp1)
grid.newpage()

# an example with ggplot
library(gtable); library(dplyr)

grid.newpage()
ggplot(data.frame(x=1, y=1), aes(x, y)) %>% ggplotGrob() %>% 
  gtable_filter("panel") %>% gList(nestvenn.obj) %>% grid.draw()


来源:https://stackoverflow.com/questions/38679718/drawing-nested-venn-diagrams

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!