Introductory Use of d3vennR
Kent Russell
2015-06-05
Venn diagrams are certainly not perfect. However, if you understand their limitations, Venn (Euler) diagrams can provide a useful way to provide context and overview. venn.js
from Ben Frederickson makes creating Venn diagrams easy by doing the math to layout the sets and leveraging d3.js
for rendering and interactivity.
Install
d3vennR
is not on CRAN and probably never will be unless someone asks me to submit, so please use devtools::install_github()
to install.
devtools::install_github("timelyportfolio/d3vennR")
Examples from venn.js
The examples included with venn.js
give us a perfect starting point to see how to make these diagrams in R
with d3vennR
. I am sure you will quickly learn that possibly the hardest part of creating a Venn diagram is determining the sets and their overlaps. If you ask/demand, then I might get motivated to add a helper function to the library. For now, we’ll just use the friendly pre-computed data provided for us.
Of course, we’ll need d3vennR
.
library("d3vennR")
Simple layout from venn.js Readme.md
d3vennR(
data = list(
list( sets = list("A"), size = 12 )
, list( sets = list("B"), size = 12 )
, list( sets = c("A", "B"), size = 2)
)
)
Changing the Style from venn.js Readme.md
styled_venn <- d3vennR(
# data from venn.js examples
# https://github.com/benfred/venn.js/blob/master/examples/medical.jsonp
data = list(
list(sets = list(0), label = 'SE', size = 28),
list(sets = list(1), label = 'Treat', size= 35),
list(sets = list(2), label = 'Anti-CCP', size = 108),
list(sets = list(3), label = 'DAS28', size=106),
list(sets = c(0,1), size=1),
list(sets = c(0,2), size=1),
list(sets = c(0,3), size=14),
list(sets = c(1,2), size=6),
list(sets = c(1,3), size=0),
list(sets = c(2,3), size=1),
list(sets = c(0,2,3), size=1),
list(sets = c(0,1,2), size=0),
list(sets = c(0,1,3), size=0),
list(sets = c(1,2,3), size=0),
list(sets = c(0,1,2,3), size=0)
)
, tasks = list(
htmlwidgets::JS('
function(){
var colours = ["black", "red", "blue", "green"];
d3.select(this).selectAll(".venn-circle path")
.style("fill-opacity", 0)
.style("stroke-width", 10)
.style("stroke-opacity", .5)
.style("stroke", function(d,i) { return colours[i]; });
d3.select(this).selectAll(".venn-circle text")
.style("fill", function(d,i) { return colours[i]})
.style("font-size", "24px")
.style("font-weight", "100");
}
')
)
)
styled_venn
styled_venn_inverted <- styled_venn
styled_venn_inverted$x$tasks <- list(
htmlwidgets::JS('
function(){
d3.select(this).selectAll(".venn-circle path")
.style("fill-opacity",0.8);
d3.select(this).selectAll("text")
.style("fill","white");
}
')
)
styled_venn_inverted
styled_venn_mono <- styled_venn
styled_venn_mono$x$tasks <- list(
htmlwidgets::JS('
function(){
d3.select(this).selectAll(".venn-circle path")
.style("fill-opacity", 0)
.style("stroke-width", 2)
.style("stroke", "#444");
d3.select(this).selectAll("text")
.style("fill", "#444");
}
')
)
styled_venn_mono
styled_venn_dropshadow <- styled_venn
styled_venn_dropshadow$x$tasks <- list(
htmlwidgets::JS('
function(){
var colours = d3.scale.category10();
var areas = d3.select(this).selectAll("g")
areas.select("path")
.filter(function(d) { return d.sets.length == 1; })
.style("fill-opacity", .1)
.style("stroke-width", 5)
.style("stroke-opacity", .8)
.style("fill", function(d,i) { return colours(i); })
.style("stroke", function(d,i) { return colours(i); });
areas.select("text").style("fill", "#444")
.style("font-family", "Shadows Into Light")
.style("font-size", "22px");
var defs = d3.select(this).select("svg").append("defs");
// from http://stackoverflow.com/questions/12277776/how-to-add-drop-shadow-to-d3-js-pie-or-donut-chart
var filter = defs.append("filter")
.attr("id", "dropshadow")
filter.append("feGaussianBlur")
.attr("in", "SourceAlpha")
.attr("stdDeviation", 4)
.attr("result", "blur");
filter.append("feOffset")
.attr("in", "blur")
.attr("dx", 5)
.attr("dy", 5)
.attr("result", "offsetBlur");
var feMerge = filter.append("feMerge");
feMerge.append("feMergeNode")
.attr("in", "offsetBlur")
feMerge.append("feMergeNode")
.attr("in", "SourceGraphic");
areas.attr("filter", "url(#dropshadow)");
}
')
)
styled_venn_dropshadow
Adding tooltips from venn.js Readme.md
Tooltips would be a great place for a helper function. This helper function is currently not in the package, but we could define one like below. Of course, there is lots of room for improvement here.
venn_tooltip <- function( venn ){
venn$x$tasks[length(venn$x$tasks)+1] <- list(
htmlwidgets::JS('
function(){
var div = d3.select(this);
// add a tooltip
var tooltip = d3.select("body").append("div")
.attr("class", "venntooltip")
.style("position", "absolute")
.style("text-align", "center")
.style("width", 128)
.style("height", 16)
.style("background", "#333")
.style("color","#ddd")
.style("padding","2px")
.style("border","0px")
.style("border-radius","8px")
.style("opacity",0);
div.selectAll("path")
.style("stroke-opacity", 0)
.style("stroke", "#fff")
.style("stroke-width", 0)
// add listeners to all the groups to display tooltip on mousover
div.selectAll("g")
.on("mouseover", function(d, i) {
// sort all the areas relative to the current item
venn.sortAreas(div, d);
// Display a tooltip with the current size
tooltip.transition().duration(400).style("opacity", .9);
tooltip.text(d.size);
// highlight the current path
var selection = d3.select(this).transition("tooltip").duration(400);
selection.select("path")
.style("stroke-width", 3)
.style("fill-opacity", d.sets.length == 1 ? .4 : .1)
.style("stroke-opacity", 1);
})
.on("mousemove", function() {
tooltip.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d, i) {
tooltip.transition().duration(400).style("opacity", 0);
var selection = d3.select(this).transition("tooltip").duration(400);
selection.select("path")
.style("stroke-width", 0)
.style("fill-opacity", d.sets.length == 1 ? .25 : .0)
.style("stroke-opacity", 0);
});
}
')
)
venn
}
venn_tooltip(d3vennR(
# data from venn.js examples
# https://github.com/benfred/venn.js/blob/master/examples/lastfm.jsonp
data = list(
list("sets"= list(0), "label"= "Radiohead", "size"= 77348),
list("sets"= list(1), "label"= "Thom Yorke", "size"= 5621),
list("sets"= list(2), "label"= "John Lennon", "size"= 7773),
list("sets"= list(3), "label"= "Kanye West", "size"= 27053),
list("sets"= list(4), "label"= "Eminem", "size"= 19056),
list("sets"= list(5), "label"= "Elvis Presley", "size"= 15839),
list("sets"= list(6), "label"= "Explosions in the Sky", "size"= 10813),
list("sets"= list(7), "label"= "Bach", "size"= 9264),
list("sets"= list(8), "label"= "Mozart", "size"= 3959),
list("sets"= list(9), "label"= "Philip Glass", "size"= 4793),
list("sets"= list(10), "label"= "St. Germain", "size"= 4136),
list("sets"= list(11), "label"= "Morrissey", "size"= 10945),
list("sets"= list(12), "label"= "Outkast", "size"= 8444),
list("sets"= list(0, 1), "size"= 4832),
list("sets"= list(0, 2), "size"= 2602),
list("sets"= list(0, 3), "size"= 6141),
list("sets"= list(0, 4), "size"= 2723),
list("sets"= list(0, 5), "size"= 3177),
list("sets"= list(0, 6), "size"= 5384),
list("sets"= list(0, 7), "size"= 2252),
list("sets"= list(0, 8), "size"= 877),
list("sets"= list(0, 9), "size"= 1663),
list("sets"= list(0, 10), "size"= 899),
list("sets"= list(0, 11), "size"= 4557),
list("sets"= list(0, 12), "size"= 2332),
list("sets"= list(1, 2), "size"= 162),
list("sets"= list(1, 3), "size"= 396),
list("sets"= list(1, 4), "size"= 133),
list("sets"= list(1, 5), "size"= 135),
list("sets"= list(1, 6), "size"= 511),
list("sets"= list(1, 7), "size"= 159),
list("sets"= list(1, 8), "size"= 47),
list("sets"= list(1, 9), "size"= 168),
list("sets"= list(1, 10), "size"= 68),
list("sets"= list(1, 11), "size"= 336),
list("sets"= list(1, 12), "size"= 172),
list("sets"= list(2, 3), "size"= 406),
list("sets"= list(2, 4), "size"= 350),
list("sets"= list(2, 5), "size"= 1335),
list("sets"= list(2, 6), "size"= 145),
list("sets"= list(2, 7), "size"= 347),
list("sets"= list(2, 8), "size"= 176),
list("sets"= list(2, 9), "size"= 119),
list("sets"= list(2, 10), "size"= 46),
list("sets"= list(2, 11), "size"= 418),
list("sets"= list(2, 12), "size"= 146),
list("sets"= list(3, 4), "size"= 5465),
list("sets"= list(3, 5), "size"= 849),
list("sets"= list(3, 6), "size"= 724),
list("sets"= list(3, 7), "size"= 273),
list("sets"= list(3, 8), "size"= 143),
list("sets"= list(3, 9), "size"= 180),
list("sets"= list(3, 10), "size"= 218),
list("sets"= list(3, 11), "size"= 599),
list("sets"= list(3, 12), "size"= 3453),
list("sets"= list(4, 5), "size"= 977),
list("sets"= list(4, 6), "size"= 232),
list("sets"= list(4, 7), "size"= 250),
list("sets"= list(4, 8), "size"= 166),
list("sets"= list(4, 9), "size"= 97),
list("sets"= list(4, 10), "size"= 106),
list("sets"= list(4, 11), "size"= 225),
list("sets"= list(4, 12), "size"= 1807),
list("sets"= list(5, 6), "size"= 196),
list("sets"= list(5, 7), "size"= 642),
list("sets"= list(5, 8), "size"= 336),
list("sets"= list(5, 9), "size"= 165),
list("sets"= list(5, 10), "size"= 143),
list("sets"= list(5, 11), "size"= 782),
list("sets"= list(5, 12), "size"= 332),
list("sets"= list(6, 7), "size"= 262),
list("sets"= list(6, 8), "size"= 85),
list("sets"= list(6, 9), "size"= 284),
list("sets"= list(6, 10), "size"= 68),
list("sets"= list(6, 11), "size"= 363),
list("sets"= list(6, 12), "size"= 218),
list("sets"= list(7, 8), "size"= 1581),
list("sets"= list(7, 9), "size"= 716),
list("sets"= list(7, 10), "size"= 133),
list("sets"= list(7, 11), "size"= 254),
list("sets"= list(7, 12), "size"= 132),
list("sets"= list(8, 9), "size"= 280),
list("sets"= list(8, 10), "size"= 53),
list("sets"= list(8, 11), "size"= 117),
list("sets"= list(8, 12), "size"= 67),
list("sets"= list(9, 10), "size"= 57),
list("sets"= list(9, 11), "size"= 184),
list("sets"= list(9, 12), "size"= 89),
list("sets"= list(10, 11), "size"= 51),
list("sets"= list(10, 12), "size"= 115),
list("sets"= list(11, 12), "size"= 162),
list("sets"= list(0, 1, 6), "size"= 480),
list("sets"= list(0, 1, 9), "size"= 152),
list("sets"= list(0, 2, 7), "size"= 112),
list("sets"= list(0, 3, 4), "size"= 715),
list("sets"= list(0, 3, 12), "size"= 822),
list("sets"= list(0, 4, 5), "size"= 160),
list("sets"= list(0, 5, 11), "size"= 292),
list("sets"= list(0, 6, 12), "size"= 122),
list("sets"= list(0, 7, 11), "size"= 118),
list("sets"= list(0, 9, 10), "size" =13),
list("sets"= list(2, 7, 8), "size"= 72)
)
))
MDS Layout from venn.js Readme.md
See this blog post from more on when to use the MDS layout
.
d3vennR(
data = list(
list(sets= list('A'), size= 9),
list(sets= list('B'), size= 15),
list(sets= list('C'), size= 8),
list(sets= list('D'), size= 6),
list(sets= list('E'), size= 9),
list(sets= list('F'), size= 9),
list(sets= list('A','B'), size= 3),
list(sets= list('A','C'), size= 0),
list(sets= list('A','D'), size= 0),
list(sets= list('A','E'), size= 0),
list(sets= list('A','F'), size= 3),
list(sets= list('B','C'), size= 3),
list(sets= list('B','D'), size= 2),
list(sets= list('B','E'), size= 0),
list(sets= list('B','F'), size= 3),
list(sets= list('C','D'), size= 2),
list(sets= list('C','E'), size= 0),
list(sets= list('C','F'), size= 0),
list(sets= list('D','E'), size= 1),
list(sets= list('D','F'), size= 0),
list(sets= list('E','F'), size= 1)
)
,layoutFunction = '
function(d) { return venn.venn(d, { initialLayout: venn.classicMDSLayout });}
'
)
Example from VennDiagram R package
As usual with R
, a lot has been done before. Let’s “recreate” some of the examples from VennDiagram
.
draw.triple.venn
library("VennDiagram")
## Loading required package: grid
grid.newpage()
grid.draw(draw.triple.venn(
area1 = 65,
area2 = 75,
area3 = 85,
n12 = 35,
n23 = 15,
n13 = 25,
n123 = 5,
category = c("First", "Second", "Third"),
fill = c("blue", "red", "green"),
lty = "blank",
cex = 2,
cat.cex = 2,
cat.col = c("blue", "red", "green")
))
venn_tooltip(
d3vennR(
data = list(
list( sets = list("First"), size = 65),
list( sets = list("Second"), size = 75),
list( sets = list("Third"), size = 85),
list( sets = list( "First", "Second"), size = 35),
list( sets = list( "Second", "Third" ), size = 15),
list( sets = list( "First", "Third" ), size = 25),
list( sets = list( "First", "Second", "Third" ), size = 5)
)
)
)
draw.quintuple.venn
library("VennDiagram")
grid.newpage()
grid.draw(draw.quintuple.venn(
area1 = 301,
area2 = 321,
area3 = 311,
area4 = 321,
area5 = 301,
n12 = 188,
n13 = 191,
n14 = 184,
n15 = 177,
n23 = 194,
n24 = 197,
n25 = 190,
n34 = 190,
n35 = 173,
n45 = 186,
n123 = 112,
n124 = 108,
n125 = 108,
n134 = 111,
n135 = 104,
n145 = 104,
n234 = 111,
n235 = 107,
n245 = 110,
n345 = 100,
n1234 = 61,
n1235 = 60,
n1245 = 59,
n1345 = 58,
n2345 = 57,
n12345 = 31,
category = c("A", "B", "C", "D", "E"),
fill = c("dodgerblue", "goldenrod1", "darkorange1", "seagreen3", "orchid3"),
cat.col = c("dodgerblue", "goldenrod1", "darkorange1", "seagreen3", "orchid3"),
cat.cex = 2,
margin = 0.05,
cex = c(1.5, 1.5, 1.5, 1.5, 1.5, 1, 0.8, 1, 0.8, 1, 0.8, 1, 0.8, 1, 0.8,
1, 0.55, 1, 0.55, 1, 0.55, 1, 0.55, 1, 0.55, 1, 1, 1, 1, 1, 1.5),
ind = TRUE
))
venn_tooltip(d3vennR(
data = list(
list(sets = list("A"), size = 301),
list(sets = list("B"), size = 321),
list(sets = list("C"), size = 311),
list(sets = list("D"), size = 321),
list(sets = list("E"), size = 301),
list(sets = list("A","B"), size = 188),
list(sets = list("A","C"), size = 191),
list(sets = list("A","D"), size = 184),
list(sets = list("A","E"), size = 177),
list(sets = list("B","C"), size = 194),
list(sets = list("B","D"), size = 197),
list(sets = list("B","E"), size = 190),
list(sets = list("C","D"), size = 190),
list(sets = list("C","E"), size = 173),
list(sets = list("D","E"), size = 186),
list(sets = list("A","B","C"), size = 112),
list(sets = list("A","B","D"), size = 108),
list(sets = list("A","B","E"), size = 108),
list(sets = list("A","C","D"), size = 111),
list(sets = list("A","C","E"), size = 104),
list(sets = list("A","D","E"), size = 104),
list(sets = list("B","C","D"), size = 111),
list(sets = list("B","C","E"), size = 107),
list(sets = list("B","D","E"), size = 110),
list(sets = list("C","D","E"), size = 100),
list(sets = list("A","B","C","D"), size = 61),
list(sets = list("A","B","C","E"), size = 60),
list(sets = list("A","B","D","E"), size = 59),
list(sets = list("A","C","D","E"), size = 58),
list(sets = list("B","C","D","E"), size = 57),
list(sets = list("A","B","C","D","E"), size = 31)
)
# feel free to skip this complicated part to match colors
, colours = htmlwidgets::JS(sprintf('
d3.scale.category10().range(%s.map(function(col){return eval(col)}))
',
jsonlite::toJSON(lapply(
c("dodgerblue", "goldenrod1", "darkorange1", "seagreen3", "orchid3")
,function(color){
rgb <- t(col2rgb(color))
sprintf("d3.rgb(%s)",paste0(rgb,collapse=","))
}
), auto_unbox=T)
)
)
,layoutFunction = '
function(d) { return venn.venn(d, { initialLayout: venn.classicMDSLayout });}
'
))