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")
Related
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.
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()
I have a plot() where multiple colour shadings represent the same thing. I would like to add a legend that conveys this by having dual-coloured boxes (see example below). Is there any way to do this using legend() or a similar command? Alternatively, is there a way to identify the precise coordinates of these boxes so I can plot a polygon() triangle over it?
Note: legend() does return the coordinates of the outer box and the top left of each labels, but I am not sure if this is sufficient to calculate where the coloured boxes are.
This is a bit of a hack, but you could put two legends on top of another. Unfortunately, there is no left triangle pch which would have been exactly as you wanted.
plot(1)
legend("bottomright",c("Label 1", "Label 2"),pch=22,col=c("red","blue"),pt.bg=c("red","blue"), pt.cex=1.8)
legend("bottomright",c("Label 1", "Label 2"),pch=21,col=c("green","orange"),pt.bg=c("green","orange"))
A slightly dirty hack can allow you to get the legend() function to give you the necessary information. A smarter person than me would probably work out how legend() calculates box positioning and replicate this outside the function. Note that editing standard R functions is probably not recommended.
If you have not edited R functions yet, an easy (and temporary) way to access it, is typing
fix(legend)
Typing
rm(legend)
later will undo your changes.
Find this section that says fill <- rep and add the lines indicated by the comments:
fillList <- NULL ## added
if (mfill) {
if (plot) {
fill <- rep(fill, length.out = n.leg)
rect2(left = xt, top = yt + ybox/2, dx = xbox, dy = ybox,
col = fill, density = density, angle = angle,
border = border)
fillList <- data.frame(left = xt, top = yt + ybox/2, dx = xbox, dy = ybox) ## added
}
xt <- xt + dx.fill
}
Find the very last line and change it to
invisible(list(rect = list(w = w, h = h, left = left, top = top),
text = list(x = xt, y = yt), fillList=fillList)) ## modified
Now call legend via
output <- legend(...) ## replace ... with whatever you want to have as inputs
and the draw triangles using the information returned by legend() like so:
with(output$fillList[1,], { ## first box
polygon(c(left, left+dx, left+dx), c(top, top, top-dy), col=myColour, border=NA)
})
I am creating violin plots with lots (will be ~100) of columns (violins). The problem is that the name of each column is very long. What I am doing current is as follows:
jpeg("stats/AllDistanceViolinPlot.jpg", width = 1000, height = 1000);
do.call(vioplot, c(lapply(data, na.omit),list(names=c("veryveryveryverylongname1", "veryveryveryverylongname2", "veryveryveryverylongname4", "veryveryveryverylongname4", "veryveryveryverylongname5", "veryveryveryverylongname6", "veryveryveryverylongname7", "veryveryveryverylongname8"))));
dev.off()
Which gives me this plot:
As you can see, the names of the columns are very long and some actually are not shown. I have also tried something without the list:
jpeg("stats/plot.jpg", width = 1000, height = 1000);
do.call(vioplot, c(lapply(data, na.omit)));
dev.off()
Which gives me this plot:
What I'd like is one of two things:
The names of the columns would be vertical so that they are shown and aren't cut off
or
Make the main plot like the second image I posted and have a separate legend that would correlate each column with the full name. For example, something like the following:
1 - veryveryveryverylongname1
2 - veryveryveryverylongname2
...
8 - veryveryveryverylongname8
Could someone please suggest the better way (or both) and comment on how to implement them?
Greatly appreciated.
Unfortunately the vioplot function in the vioplot package does not accept the usual base graphics parameters for modifying the orientation of axis annotation. You will need to make a new vioplot function and change this code:
if (!horizontal) {
if (!add) {
plot.window(xlim = xlim, ylim = ylim)
axis(2)
axis(1, at = at, label = label)
To this:
if (!horizontal) {
if (!add) {
plot.window(xlim = xlim, ylim = ylim)
axis(2)
axis(1, at = at, label = label , las=2)
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)