Adding legend to venn diagram - r

I am using library VennDiagram to plot venn diagrams. But this function does not have a functionality to add legend and set names are displayed on or close to the sets themselves.
library(VennDiagram)
x <- list(c(1,2,3,4,5),c(4,5,6,7,8,9,10))
venn.diagram(x,filename="test.png",fill=c("#80b1d3","#b3de69"),
category.names=c("A","B"),height=500,width=500,res=150)
And with many sets, overplotting names is an issue and I would like to have a legend instead. The function is built on grid graphics and I have no idea how grid plotting works. But, I am attempting to add a legend anyway.
Looking into the venn.diagram function, I find that final plotted object is grob.list and it is a gList object and its plotted using grid.draw().
png(filename = filename, height = height, width = width,
units = units, res = resolution)
grid.draw(grob.list)
dev.off()
I figured out that I could create a legend by modifying the venn.diagram function with the code below.
cols <- c("#80b1d3","#b3de69")
lg <- legendGrob(labels=category.names, pch=rep(19,length(category.names)),
gp=gpar(col=cols, fill="gray"),byrow=TRUE)
Draw the object lg
png(filename = filename, height = height, width = width,
units = units, res = resolution)
grid.draw(lg)
dev.off()
to get a legend
How do I put the venn diagram (gList) and the legend (gTree,grob) together in a usable way? I am hoping to get something like base plot style:
or the ggplot style

If you are allowed to use other packages than VennDiagram, I suggest the following code using the eulerr package:
library(eulerr)
vd <- euler(c(A = 5, B = 3, "A&B" = 2))
plot(vd, counts = TRUE,lwd = 2,
fill=c("#80b1d3","#b3de69"),
opacity = .7,
key = list( space= "right", columns=1))
with key you define the legend location and appearance.

If you want to continue using the VennDiagram package and learn a bit of grid on the way:
Prepare diagram and legend
library(VennDiagram)
x <- list(c(1,2,3,4,5),c(4,5,6,7,8,9,10))
diag <- venn.diagram(x,NULL,fill=c("#80b1d3","#b3de69"),
category.names=c("A","B"),height=500,width=500,res=150)
cols <- c("#80b1d3","#b3de69")
lg <- legendGrob(labels=c("A","B"), pch=rep(19,length(c("A","B"))),
gp=gpar(col=cols, fill="gray"),
byrow=TRUE)
Transform the diagram to a gTree
(I'd love to find a better way if anyone knows one)
library(gridExtra)
g <- gTree(children = gList(diag))
Plot the two gTrees side by side
gridExtra::grid.arrange(g, lg, ncol = 2, widths = c(4,1))
Or one above the other
grid.arrange(g, lg, nrow = 2, heights = c(4,1))

I have found a solution as well, but the venn diagram region is not square aspect ratio. And the legend is not spaced ideally.
library(gridGraphics)
png("test.png",height=600,width=600)
grab_grob <- function(){grid.echo();grid.grab()}
grid.draw(diag)
g <- grab_grob()
grid.arrange(g,lg,ncol=2,widths=grid::unit(c(0.7,0.3),"npc"))
dev.off()

Related

Join two graphs (one beside other) GWAS

I have this code to generate 2 different types of graphs (manhattan plot and a QQ plot)
# Set up the work directory in which all data is gonna be extracted
gwasResults2 = read.csv("DWStem.csv") #Change name of the file
library(qqman) #Run to create plots
library(cowplot)
library(extrafont)
library(grid)
library(cowplot)
library(gridExtra)
MH <- manhattan(gwasResults2, chr="CHR", bp="BP", snp="SNP", p="P",
col = c("chartreuse2", "darkorange1", "gold1"),ylim=c(0,-log10(1e-06)), chrlabs = NULL,
suggestiveline = -log10(1e-03), genomewideline = -log10(1e-05),
highlight = NULL, logp = TRUE, annotatePval = NULL,
annotateTop = TRUE, main='DWStem')
QQ <- qq(gwasResults2$P, main='DWStem', pch = 24, cex=1, col="gold", bg="brown1", lwd=1, xlim=c(0,5), ylim=c(0,5)) #Run to create qqplot $P need to be there!
Total <- plot_grid(MH, QQ, labels = c("a", "b"), ncol = 2)
But apparently I cant put them aside each other because I get the next error:
Error in plot_to_gtable(x) :
Argument needs to be of class "ggplot", "gtable", "grob", "recordedplot", or a function that plots to an R graphicsdevice when called, but is a list
Any idea of how I can solve it?
In advance, thank you! :D
The functions manhattan and qq produce base graphics, not grid graphics. You need to use base graphics methods for the layout. For example, using reproducible data,
par(mfrow=c(1,2))
manhattan(gwasResults, main = "a")
qq(gwasResults$P, main = "b")
produces
If your plots used grid graphics (produced by grid, ggplot2 or lattice), your method would have worked. If some use grid and some use base graphics, it is possible to mix them in the same display, but it is not easy. See the gridBase and gridGraphics packages.
EDITED to add:
If you have gridGraphics installed, then it's actually not so bad to mix the base graphics with grid graphics. You just set MH and QQ to be functions producing the graphs, rather than the graphs themselves. For example,
MH <- function() { manhattan(gwasResults) }
QQ <- function() { qq(gwasResults$P) }
Total <- plot_grid(MH, QQ, labels = c("a", "b"), ncol = 2)
When you print Total, you get this:
The graphs have lost their y axis labels, but otherwise look okay.

Quenstions about rgl layout3d plot3d

I am trying to add a overall title to a 3d plot with multiple subplots using the rgl package from R,and i am also trying to set the distance between two sub scenes. In 2D plot, we can use title("my title",outer=TRUE) to gave an overall title, and "mar" to set the margin. So, what is the corresponding parameters?
Here are my code:
rgl.viewpoint(0,0,fov=0)
par3d(windowRect=c(50,50,700,700),zoom=0.8)
mat<-matrix(c(1,2,3,4,5,6),3,2,byrow = TRUE)
height<-c(2,2,2)
width<-c(1,1)
layout3d(mat, height = height,width=width,sharedMouse = TRUE)
for (i in 1:6) {
next3d()
shade3d(shapes[[i]], col = col[i])
}
I want to put a title in the picture and adjust the distance between two subplots.
There's no concept of outer regions in rgl. What you need to do is to add another region to your layout, and put the title there. For example,
library(rgl)
open3d()
mat<-matrix(c(7,7,1,2,3,4,5,6),4,2,byrow = TRUE)
height<-c(1,2,2,2)
width<-c(1,1)
layout3d(mat, heights = height, widths=width, sharedMouse = TRUE)
for (i in 1:6) {
next3d()
shade3d(cube3d(), col = i)
}
next3d()
text3d(0,0,0,"My title")

Combine phylogenetic tree with x,y graph

I'm trying to arrange a phylogenetic tree onto a graph showing physiological data for a set of related organisms. Something like the picture below. This was put together in powerpoint from 2 separate graphs. I guess it gets the job done, but I was hoping to create a single image which I think will be easier to format into a document. I am able to produce the graph I want using ggplot2, and import the tree using ape. I was thinking there should be a way to save the tree as a graphical object and then arrange it with the graph using the gridarrange function in gridExtra. The problem is that ape won't let me save the tree as a graphical object, e.g.,
p2<-plot(tree, dir = "u", show.tip.label = FALSE)
just plots the tree and when you call p2 it just gives a list of arguments. I'm wondering if anyone has any tips.
Thanks!
I'm not sure if that will work with gtable from CRAN
require(ggplot2)
require(gridBase)
require(gtable)
p <- qplot(1,1)
g <- ggplotGrob(p)
g <- gtable_add_rows(g, unit(2,"in"), nrow(g))
g <- gtable_add_grob(g, rectGrob(),
t = 7, l=4, b=7, r=4)
grid.newpage()
grid.draw(g)
#grid.force()
#grid.ls(grobs=F, viewports=T)
seekViewport("layout.7-4-7-4")
par(plt=gridPLT(), new=TRUE)
plot(rtree(10), "c", FALSE, direction = "u")
upViewport()
first I'd like to thanks baptiste for ALL his multiple answers that solved most of my issues with ggplot2.
second, I had a similar question which was to include a tree from ape inside a heatmap obtained with ggplot2. Baptiste made my day, and though my simplified version could help.
I used only what was useful for me (removing the addition of gg_rows).
library(ape)
tr <- read.tree("mytree.tree")
# heat is the heatmap ggplot, using geom_tile
g <- ggplotGrob(heat)
grid.newpage()
grid.draw(g)
# use oma to reduce the tree so it fits
par(new = TRUE, oma = c(5, 4, 5, 38))
plot(tr)
nodelabels(tr$node.label, cex = 1, frame = "none", col = "black", adj = c(-0.3, 0.5))
add.scale.bar()
# use dev.copy2pdf and not ggsave
dev.copy2pdf(file = "heatmap_prob.pdf")
the result is here

to display two heatmaps in same pdf side by side in R

i am trying to display two or more heatmaps side by side in the same png or pdf . The layout or mfcol is not working in the case. Can someone please help me out with this.
Here's one option using the recently introduced gridGraphics package,
library(gridGraphics)
library(grid)
heatmap(as.matrix(mtcars))
library(gridGraphics)
grab_grob <- function(){
grid.echo()
grid.grab()
}
g <- grab_grob()
grid.newpage()
# library(gridExtra)
# grid.arrange(g,g, ncol=2, clip=TRUE)
lay <- grid.layout(nrow = 1, ncol=2)
pushViewport(viewport(layout = lay))
grid.draw(editGrob(g, vp=viewport(layout.pos.row = 1,
layout.pos.col = 1, clip=TRUE)))
grid.draw(editGrob(g, vp=viewport(layout.pos.row = 1,
layout.pos.col = 2, clip=TRUE)))
upViewport(1)
As stated in the help document for heatmap.2,
'heatmap.2()' uses 'layout' and draws the 'image' in the lower
right corner of a 2x2 layout. Consequentially, it can not be
used in a multi column/row layout, i.e., when 'par(mfrow= *)' or
'(mfcol= *)' has been called.
The same is true for heatmap.
Here's a way of doing that. It is very hacky but I think that when a function doesn't do what you want to do the best solution is to make it do it anyway.
Function heatmap.2 contains the following three lines at mid-way through its code:
...
op <- par(no.readonly = TRUE)
on.exit(par(op))
layout(lmat, widths = lwid, heights = lhei, respect = FALSE)
...
It is because of them that you can't use layout and par(mar=...) since it overrides it.
Copy the code of heatmap.2 into a new function (say heatmap.3) and remove those three lines:
heatmap.3 <- function(... #etc etc with the exact same code minus those 3 lines
Then your code to produce your two heatmaps side by side will be, for instance:
layout(rbind(c(4,3,8,7),c(2,1,6,5)),
widths = c(1,2,1,2), heights = c(1,2), respect = FALSE)
heatmap.3(x)
heatmap.3(y)
When preparing your layout remember that the heatmap code plot first the heatmap itself, then the "row" dendrogram, then the "col" dendrogram and finally the histogram, hence the order from top to bottom, left to right is 4, 3, 2, 1 meaning when both heatmap are side by side it becomes 4, 3, 8, 7, 2, 1, 6, 5.
After having exactly the same problem, I came up with the following solution:
1) Use ggplot2 to make your heatmap with dendrogram like here: Reproducing lattice dendrogram graph with ggplot2 and then arrange it with multiplot() function (http://www.cookbook-r.com/Graphs/Multiple_graphs_on_one_page_%28ggplot2%29/)
2) However, this is a lot of work and I wanted to stick with the base heatmap() function. The following is easy (though not plain R) and works on Linux if you have imagemagick installed:
m <- matrix(runif(10^2), ncol=10)
for (i1 in 1:4) {
ifile <- paste0(i1,'_heatmap.pdf')
pdf(ifile)
heatmap(m)
d <- dev.off()
}
system('montage -geometry 100% -tile 2x2 ./*_heatmap.pdf outfile.pdf')

R par(omd) does not contain within mfrow

I would like two plots to show up in two seperate spaces in the plot so I do:
par(mfrow=c(1,2))
plot(1:10,1:10)
Now I would like the second plot to be about 25% shorter than the first plot so I adjust omd:
tmp <- par()$omd
tmp[4] <- 0.75
par(omd=tmp)
plot(1:10,1:10)
The problem is that the second plot shows up ontop of the first plot. How do I avoid this margin issue?
Maybe try using layout instead?
layout(matrix(c(1, 1, 0, 2), ncol = 2L), widths = c(1,1),heights = c(0.5,1))
par(mar = c(3,2,2,2))
plot(1:10,1:10)
par(mar = c(3,2,2,2))
plot(1:10,1:10)
I guess maybe you'd want to set the heights to c(0.2,0.8) to get a 25% reduction?
Edit
But I don't think that omd does what you think it does. It changes the region inside the outer margins, which will always include both plot regions when setting par(mfrow = c(1,2)). What you really want to change, I think is plt, which alters the size of the current plotting region (using quartz, as I'm on a mac):
quartz(width = 5,height = 5)
par(mfrow=c(1,2))
vec <- par("plt")
plot(1:10,1:10)
par(plt = vec * c(1,1,1,0.75))
plot(1:5,1:5)

Resources