Side-by-side Venn diagram using Vennerable - r

I am trying to put two Venn diagrams in one single graph, i.e. I am using par(mfrow=c(1,2)) at the very beginning. However, when I use the Venn() function in the Vennerable package:
VennCompare = Venn(SetNames = c("A", "B", "C"), Weight = c(0, 38, 1, 0, 1, 80, 0, 14))
pdf(file="Venn.pdf", width=12, height=6)
par(mfrow=c(1,2))
plot(VennCompare, doWeights=FALSE)
plot(VennCompare, doWeights=TRUE, show = list(SetLabels = TRUE, Faces = FALSE))
dev.off()
The resultant pdf file contains 2 pages, and each page has a single Venn diagram.
How can I put the two diagrams into a single page (i.e. side-by-side)?

As already discussed in comments, Vennerable uses grid graphics and fixes the grid parameters inside of the package functions. You should probably ask kindly from package maintainers if they could add this kind of functionality in their packages, but in a meantime I offer you a Sketchof a hack which allows you to do what you want:
The first command allows you to edit the function called makevp.eqsc which seems to contain the grid definitions:
trace("makevp.eqsc",edit=TRUE)
Original code looks like this:
function (xrange, yrange)
{
pushViewport(plotViewport(name = "Vennmar", c(1, 1, 1, 1)))
pushViewport(viewport(name = "Vennlay", layout = grid.layout(1,
1, widths = diff(xrange), heights = diff(yrange), respect = TRUE)))
pushViewport(viewport(name = "Vennvp", layout.pos.row = 1,
layout.pos.col = 1, xscale = xrange, yscale = yrange))
}
The most relevant parts are grid.layout, which tells you what kind of grid you want to draw. Also layout.pos.row and layout.pos.col are important, they tell in which position to draw. Change the code for example like this:
function (xrange, yrange)
{
pushViewport(plotViewport(name = "Vennmar", c(1, 1, 1, 1)))
pushViewport(viewport(name = "Vennlay", layout = grid.layout(2,
1, widths = diff(xrange), heights = diff(yrange), respect = TRUE)))
pushViewport(viewport(name = "Vennvp", layout.pos.row = number,
layout.pos.col = 1, xscale = xrange, yscale = yrange))
}
Now you will get two stacked graphs, like this:
number<-1 #change the argument inside of makevp.eqsc
plot(VennCompare, doWeights=FALSE)
number<-2
plot(VennCompare, doWeights=TRUE,
show = list(SetLabels = TRUE, Faces = FALSE),add=TRUE) #note add=TRUE
This doesn't look really nice, but by modifying makevp.eqsc you can probably archieve more nice results.

I couldn't install that package, but a trick that might help here is to use grid.grab to capture the drawing into a grob that can be placed elsewhere,
library(grid)
myplot <- function(){
pushViewport(viewport(x=0.5,width=1, just=0.5))
grid.rect(gp=gpar(fill=grey(runif(1, 0.2, 0.8))))
grid.points()
popViewport()
}
p1 <- grid.grabExpr(myplot())
p2 <- grid.grabExpr(myplot())
library(gridExtra)
grid.arrange(p1, p2, ncol=2)

Try this:
v <- Venn(n=2)
plot(v)
grid.text("Title", vp = viewport(x=0.5, y=.9, w=unit(1, "npc"), h=unit(1, "npc")))

Related

Adding scale bar to map made with ggRGB

I have made a RGB composite image of a satellite image using ggRGB() from the RStoolbox package. I would like to add a scale bar to the image, but I'm stumped as to how to do this. I would usually use scalebar() from the ggsn package when working with ggmaps() in R, but it doesn't look like it can handle a RasterBrick object as input like like is required for ggRGB().
Here is an example:
library(raster)
library(ggplot2)
library(RStoolbox)
data(lsat)
ggRGB(img = lsat,
r = 3,
g = 2,
b = 1,
stretch = 'hist') +
blank() # eliminate x and y axes
This produces the following image:
I would like to add a scale bar in the upper right corner. Here's what I tried:
ggRGB(img = lsat,
r = 3,
g = 2,
b = 1,
stretch = 'hist') +
blank() +
ggsn::scalebar(lsat, dist = 2, dist_unit = "km",
transform = TRUE, model = "WGS84", location = "upperright")
This returns an error: "Error in .local(x, ...) : invalid layer names"
Any help would be much appreciated. I'd like to stick with ggRGB() if possible, but I'd be open to other plotting methods if I can place a scale bar on the image.
You could use package ggspatial
ggRGB(img = lsat,
r = 3,
g = 2,
b = 1,
stretch = 'hist') +
theme_void() +
ggspatial::annotation_scale(location = "tr", width_hint =0.5, pad_x = unit(0.7, "cm"))
If you want more fine control over the appearance, a slightly more verbose but highly customizable method is also given in this answer https://stackoverflow.com/a/39069955/2761575

How to plot the restults of ctree in grid?

The results of the plot can be normally arranged in grids. I currently have an issue by plotting the results of the ctree function from the party package in a grid. This question is a duplicate of a question from 6 years and 8 months ago (Plot of BinaryTree (ctree, party) ignores plot option of par()). It was opted that gridExtra could provide a solution. However, till now no solution for this issue has been given. Consider the example below.
library(party)
library(gridExtra)
#Create random dataframe
dfA <- data.frame(x=c(rnorm(50, 5), rnorm(50, 2)),
y=c(rbinom(50, 1, .9), rbinom(50, 1, .1)))
#Duplicate dataframe
dfB <- dfA
#Plot in base R wit par (does not work)
par(mfrow = c(1, 2))
plot(party::ctree(y~x, data=dfA))
plot(party::ctree(y~x, data=dfB))
#Try to organize in a grid wit gridExtra (does not work)
treeA <- party::ctree(y~x, data=dfA)
treeB <- party::ctree(y~x, data=dfB)
grobA <- arrangeGrob(plot(treeA))
grobB <- arrangeGrob(plot(treeB))
grid.arrange(grobA, grobB, ncol=2)
Error in gList(list(wrapvp = list(x = 0.5, y = 0.5, width = 1, height = 1, :
only 'grobs' allowed in "gList"
The arrangeGrob(plot(treeA)) and arrangeGrob(plot(treeB)) also return an error stating Error in vapply(x$grobs, as.character, character(1)) : values must be length 1, but FUN(X[[1]]) result is length 0
Does someone known how plot the results of the ctree function in a grid?
Thank you in advance.
## grab the scene as a grid object
library(gridExtra)
library(gridGraphics)
library(grid)
list.to.pass <- list('plot(ctree(y~x, data=dfA))',
'plot(ctree(y~x, data=dfB))')
out<-list()
for (i in c(1,2)){
print(i)
formula(list.to.pass[[i]])
out[[i]] <- grid.grab()
print(out[[i]])
dev.off()
}
grid.arrange(out[[1]], out[[2]], nrow = 1,ncol=2)
You will get:
The plots in party and its successor package partykit are implemented in grid and hence the base graphics options from par() such as mfrow do not work. You can use grid.layout() to achieve similar results. Doing so in plain grid is a bit technical but the code should not be too hard to follow:
grid.newpage()
pushViewport(viewport(layout = grid.layout(1, 2)))
pushViewport(viewport(layout.pos.row = 1, layout.pos.col = 1))
plot(treeA, newpage = FALSE)
popViewport()
pushViewport(viewport(layout.pos.row = 1, layout.pos.col = 2))
plot(treeB, newpage = FALSE)
popViewport()
The reason for the newpage = FALSE argument is that by default the plot is drawn on a new page, rather than adding to a potentially existing plot.

How to edit a previous plot in an R graphics device split by mfrow

I have an existing plotting function (perhaps written by someone else) that uses mfrow to plot multiple figures on the same graphics device. I want to edit figures that have already been plotted (e.g. perhaps add a reference line to figure 1)
par(mfrow = c(1, 2))
plot(1:10)
hist(1:10)
# Oh no! I want to add abline(a = 0, b = 1) to the first plot!
Assume this code is nested in another plotting function
PlotABunchOfStuff(1:10) that I can't modify.
I don't want to modify PlotABunchOfStuff because someone else owns it, or I'm just debugging and won't need the extra details once the bug is found.
Use par(mfg).
For example:
par(mfrow = c(2, 3))
for (i in 1:6) {
plot(i, xlim = c(0,7), ylim = c(0, 7))
}
par(mfg = c(2, 2))
points(3,3,col= "red")
par(mfg = c(1, 1))
points(3,3,col= "blue")
If you are ready to use ggplot I think you can find what you want in the code below :
df <- data.frame(x = 1:10, y = 1:10)
g1 <- ggplot(df, aes(x = x, y = y)) +
geom_point()
g2 <- ggplot(df, aes(x = x, y = y)) +
geom_line()
grid.arrange(g1, g2)
g1 <- g1 + geom_smooth(method='lm',formula=y~x) # it could be anything else !
grid.arrange(g1, g2)
Edit 1
Create a graphical object in windows which will be destroye after dev.off() if filename = "" :
win.metafile(filename = "")
By default inhibit doesn't allow the plot to be recorded so we use enable :
dev.control('enable')
plot(1:10)
p <- recordPlot()
dev.off()
replayPlot(p)
p
abline(a = 1, b = 1, col = "red")
p <- recordPlot()
dev.off()
replayPlot(p)
My inspirations on Stackoverflow :
R plot without showing the graphic window
Save a plot in an object
My inspirations on R :
https://www.rdocumentation.org/packages/grDevices/versions/3.6.0/topics/dev
https://www.rdocumentation.org/packages/grDevices/versions/3.6.0/topics/recordPlot
I hope it helps you ! Good luck.
Be careful with scaling!
For the example in the original question, this will not have the result I think is desired.
par(mfrow = c(1, 2))
plot(1:10)
hist(1:10)
par(mfg = c(1, 1))
abline(a = 0, b = 1)
But this will have the result I think is desired.
par(mfrow = c(1, 2))
plot(1:10)
hist(1:10)
par(mfg = c(1, 1))
plot.window(xlim = c(1, 10), ylim = c(1, 10))
abline(a = 0, b = 1)
The mfg graphics parameter will allow you to jump to any panel, but the window scaling may need to be adjusted to be appropriate for the scale used when the original plot was created in that panel.

Ploting 3 maps in 2 rows using R tmap package

I'm trying to save a plot using 3 maps made by the tmap package, with the larger one at the top, and the other 2 at the bottom like the example above:
But using tmap_arrange() provided by the package for this kind of procedure, it gives me the followig:
data(World)
p1 <- tm_shape(World)+tm_polygons()
p2 <- tm_shape(World[World$continent=='South America',])+tm_polygons()
p3 <- tm_shape(World[World$name=='Brazil',])+tm_polygons()
tmap_arrange(p1,p2,p3,nrow=2)
I've tried to use many options, like export the maps as images and then import again to R to compose a full image using par() and/or split.screen(), but also doesn't work properly.
There is any way to work around this and get the wanted result?
Thanks in advance!
One hackish way would be to use the grid package functionality. Grab the output of each plot/map and store it as a gTree object and then try to arrange the new objects in a grid.
library(tmap)
library(cowplot) # for plot_grid() function - good to arrange multiple plots into a grid
library(grid)
library(gridGraphics)
data(World)
tm_shape(World) + tm_polygons()
g1 <- grid.grab()
tm_shape(World[World$continent == 'South America', ]) + tm_polygons()
g2 <- grid.grab()
tm_shape(World[World$name == 'Brazil', ]) + tm_polygons()
g3 <- grid.grab()
# Try to arrange the plots into a grid using cowplot::plot_grid().
# First bind the p2 and p3 as one plot;
# adjust distance between them by forcing a NULL plot in between.
p23 <- plot_grid(g2, NULL, g3, rel_widths = c(1, -0.7, 1), nrow = 1)
plot_grid(g1, p23, nrow = 2, scale = c(0.8, 1))
I could not figure it out how to make it respond to the align argument though :/ But maybe this puts you in some exploring direction or others can edit/improve this answer.
# Save the plot
ggsave(filename = "tmap-arrange-grid-1.png",
width = 10, height = 6, units = "cm", dpi = 150)
Note that, initially I thought that I could explore with adding a NULL object to tmap_arrange like tmap_arrange(p1, NULL, p2, p3, nrow = 2), but unfortunately, it does not accept it.
Another approach, inspired from this related question could be something along these lines:
library(grid)
grid.newpage()
pushViewport(viewport(layout = grid.layout(nrow = 2, ncol = 2)))
print(p1, vp = viewport(layout.pos.row = 1, layout.pos.col = 1:2))
print(p2, vp = viewport(layout.pos.row = 2, layout.pos.col = 1))
print(p3, vp = viewport(layout.pos.row = 2, layout.pos.col = 2))
Again, here, I didn't have the time to explore with aligning the plots perfectly, but others might improve this answer.

Plot multiple 3D images in a single plot window with plot3d

When I plot several 3D images using plot3d from the rgl package, the images are displayed separately. I want to show them in one plot, as when using, e.g., par(mfrow=c(2, 2)) to display four 2D images in a single plot window.
Is this possible?
The command layout3d might be useful. Maybe this code can help:
shapes <- list(Tetrahedron = tetrahedron3d(), Cube = cube3d(), Octahedron = octahedron3d(),
Icosahedron = icosahedron3d(), Dodecahedron = dodecahedron3d(),
Cuboctahedron = cuboctahedron3d())
col <- rainbow(6)
open3d()
mat <- matrix(1:4, 2, 2)
mat <- rbind(mat, mat + 4, mat + 8)
layout3d(mat, height = rep(c(3, 1), 3), sharedMouse = TRUE)
for (i in 1:6) {
next3d()
plot3d(shapes[[i]], col = col[i])
next3d()
text3d(0, 0, 0, names(shapes)[i])
}
To deactivate the rotation of all the solids together, it is enough to put sharedMouse = FALSE.

Resources