22

Suppose I have two vectors

foo <- c('a','b','c','d')
baa <- c('a','e','f','g')

Does anyone know of a way to produce a venn diagram but have the vector items visualised within the diagram.

Like so? (made in powerpoint) enter image description here

A_Skelton73
  • 1,150
  • 4
  • 12
  • 23
  • 1
    `venn.diagram` from the `VennDiagram package` is based on `grid` graphics and the function returns the object. This allows you to go in and manually change the labels to show the items rather than the counts. – user20650 Jul 29 '14 at 16:55
  • Have you studied this [excellent example](http://stackoverflow.com/a/8172731/1305688) using the venneuler package? – Eric Fail Jul 29 '14 at 19:13

3 Answers3

17

A quick solution using the venn.diagram function from the VennDiagram package. The labels (counts) are hard coded in the function so can't be changed using function arguments. But for a simple example like this you can change the grobs yourself.

library(VennDiagram)

# your data
foo <- c('a','b','c','d')
baa <- c('a','e','f','g')

# Generate plot
v <- venn.diagram(list(foo=foo, baa=baa),
                  fill = c("orange", "blue"),
                  alpha = c(0.5, 0.5), cat.cex = 1.5, cex=1.5,
                  filename=NULL)

# have a look at the default plot
grid.newpage()
grid.draw(v)

# have a look at the names in the plot object v
lapply(v,  names)
# We are interested in the labels
lapply(v, function(i) i$label)

# Over-write labels (5 to 7 chosen by manual check of labels)
# in foo only
v[[5]]$label  <- paste(setdiff(foo, baa), collapse="\n")  
# in baa only
v[[6]]$label <- paste(setdiff(baa, foo)  , collapse="\n")  
# intesection
v[[7]]$label <- paste(intersect(foo, baa), collapse="\n")  

# plot  
grid.newpage()
grid.draw(v)

Which produces

enter image description here

Obviously this method would quickly get out of hand with more categories and intersections.

user20650
  • 24,654
  • 5
  • 56
  • 91
  • 1
    Does anyone have any feedback about how this could be done programatically? I've been generating venn.diagrams and trying to decipher the relationship between the order of labels and the category labels, the "foo" and "baa" in the above example. That way i could atleast match based on identity. – bw4sz Dec 08 '15 at 22:52
  • @bw4sz; ive not looked at this recently but i seem to remember (and is noted in the answer) that this method wont scale ... I couldn't see a easy way to identify the index of each label. Might be worth asking a new Q. – user20650 Dec 08 '15 at 23:04
  • 2
    For a 3-way Venn, this worked for me. Its an amalgam of several folks code from Stack, apologies for not having the reference. The thing is labels **start** at position 7 in the list for Venn's with 3 circles, hence the `i+6`. `grid.draw(ven.plot) overlaps <- calculate.overlap(named.list.for.venn) overlaps <- overlaps[ sort(names(overlaps)) ] for (i in 1:length(overlaps)){ lab <- paste0("a", i) ven.plot[[i+6]]$label <- paste(overlaps[[i]], collapse = "\n") } grid.newpage() grid.draw(ven.plot)` – Scott Jun 23 '16 at 15:23
  • 3
    Scott. please fell free to edit the answer to apply your method to the example or expand it with a new example. Or of course, you could add a new answer: save it hiding away in the comments. – user20650 Jun 23 '16 at 17:48
  • @user20650 In this solution there is a way to show a label name also for the intersection? So for e.g. foo_baa – Will Feb 03 '21 at 18:23
  • @user20650 and also if there is a way to set the circles size – Will Feb 03 '21 at 18:31
  • 1
    @Will; re intersection label; not by editing the plot as there is no relevant grob. But you can just add another text grob manually e.g. `grid.text("foo_bar", x=0.5, y=0.8)` -- you could programmatically try to find the `x` and `y` – user20650 Feb 03 '21 at 19:08
  • 1
    @Will; re circle size; have a look at the options of `?venn.diagram` ; quick glance suggests `area.vector` may be relevant. Although perhaps it is enough to just reduce the dimensions of your plot device to get a smaller plot hence smaller circles. – user20650 Feb 03 '21 at 19:12
13

Using the RAM package:

library(RAM)
foo <- c('a','b','c','d')
baa <- c('a','e','f','g')
group.venn(list(foo=foo, baa=baa), label=TRUE, 
    fill = c("orange", "blue"),
    cat.pos = c(0, 0),
    lab.cex=1.1)

enter image description here

Marco Sandri
  • 23,289
  • 7
  • 54
  • 58
  • @A_Skelton73 ; probably worth giving this the tick now – user20650 Aug 27 '17 at 19:59
  • 2
    It only supports 2 groups to have actual item labels – Fábio Mar 06 '18 at 19:42
  • Is it possible to split labels into multiple columns? – eod Dec 15 '21 at 17:20
  • @eod A very simple (and raw) solution is `group.venn(list("Row1\nRow2"=foo, "baa"=baa), label=TRUE, fill = c("orange", "blue"), cat.pos = c(0, 0), lab.cex=1.1)` – Marco Sandri Dec 15 '21 at 19:12
  • Hmmm, I applied your version and it doesn't intersect. Two circles become independent. – eod Dec 15 '21 at 20:14
  • @eod No. With my code I get this image https://imgur.com/a/AEIYsN5 – Marco Sandri Dec 15 '21 at 20:36
  • Oh, sorry. You are right. I used the wrong vocabulary. It is items in the circles that I want to split. For example in the yellow circle "b" and "c" would be in one column and "d" in another. The same for items in the blue circle. I have ~100 items in my case in each circle, so I would need to split them into 10 columns and 10 rows. – eod Dec 15 '21 at 20:55
4

Another way, which I find simpler is using the ggvenn package with show_elements = TRUE.

library(ggvenn)
library(RColorBrewer)
AA <- c("hi","foo", "bar","yep","woo","hoo")
BB <- c("baa","yep", "woo","yes")
CC <- c("yes","foo","hi","woo", "huh")

x <- list(AA=AA , BB=BB , CC=CC)

ggvenn(x, show_elements = T, label_sep = "\n", fill_color = brewer.pal(name="Set2",n=3))

The resulting image

Jan
  • 4,974
  • 3
  • 26
  • 43
Tal Zaquin
  • 63
  • 5