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.
Related
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.
I created a few plots with the filled.contour function. Then I would like to plot two of the plots next to each other. Therefore I used the grid.arrange function.
This is my code:
install.packages("gridExtra")
install.packages("lattice")
install.packages("grid")
library(lattice)
library(gridExtra)
library(grid)
# Fake data
x <- c(1:10)
y <- c(1:10)
z<-matrix(data=c(1:100), nrow=10, ncol=10, byrow = FALSE)
p1<-filled.contour(x,y,z, color = terrain.colors, asp = 1) # simple
# Lay out both plots
grid.arrange(p1,p1, ncol=2)
But what I get is:
Error in gList(list(wrapvp = list(x = 0.5, y = 0.5, width = 1, height
= 1, : only 'grobs' allowed in "gList"
Thats why I tried this:
install.packages("gridExtra")
install.packages("lattice")
install.packages("grid")
library(lattice)
library(gridExtra)
library(grid)
# Fake data (taken from the fill.contour help examples)
x <- c(1:10)
y <- c(1:10)
z<-matrix(data=c(1:100), nrow=10, ncol=10, byrow = FALSE)
p1<-filled.contour(x,y,z, color = terrain.colors, asp = 1) # simple
p1<-grob(p1)
is.grob(p1)
# Lay out both plots
grid.arrange(p1,p1, ncol=2)
But this does not work either. Can you help me please?
As #eipi10 pointed out, filled.contour is base graphics, so you should use base arrangement functions, i.e. par(mfrow = c(1,2)) to arrange two plots side by side.
EDIT: apparently filled contour is famous for defeating all layout attempts. I tried par(plt...) layout() and par(mfrow...) I found filled.countour3 as a workaround as described here:
http://wiki.cbr.washington.edu/qerm/sites/qerm/images/e/ec/Example_4_panel_contour_plot_with_one_legend.R
and in question 14758391 on this site. Sorry for the confusion
I would like to plot the BinaryTree in the uppper part of the plot, and make a second one in the second part (bottom). Here is some example code to show, that the plot of the tree completely ignores the partitioning options set by par()
library("party")
### regression
airct <- ctree(Ozone ~ ., data = subset(airquality, !is.na(Ozone)))
### classification
irisct <- ctree(Species ~ .,data = iris)
par(mfrow = c(2, 1))
plot(airct)
plot(irisct)
This code does not plot the two trees in the same plot (page). How can I correct this?
Even when following the very detailed answer does not work in this case: plots generated by 'plot' and 'ggplot' side-by-side
the plotting of a ctree ignores all established options.
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. In addition to the remarks from the comments, 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(2, 1)))
pushViewport(viewport(layout.pos.row = 1, layout.pos.col = 1))
plot(airct, newpage = FALSE)
popViewport()
pushViewport(viewport(layout.pos.row = 2, layout.pos.col = 1))
plot(irisct, 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.
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")))
I am using a package (treemap) that uses grid package to produce a treemap. However, I would like to plot several of these treemaps together, to add different color schemes to these plots. tmPlot function uses grid.newpage function, which clears the graphics window. I have not found a way to save grid.newpage objects as you can do for ggplot2objects. Is there a way to plot several grid.newpage objects to a same window?
## Example
library(treemap)
# load Gross national income data
data(GNI2010)
size <- aggregate(GNI ~ continent, GNI2010, sum)
size <- size[with(size, order(GNI, decreasing = T)),]
cont <- size$continent
widths <- c(sum(size[c(1,3,5),]$GNI),
sum(size$GNI) - sum(size[c(1,3,5),]$GNI))
heights <- c(sum(size[c(1,2),]$GNI),
sum(size[c(3,4),]$GNI),
sum(size[c(5,6),]$GNI))
palettes <- c("Greens", "Blues", "Reds", "Oranges", "Purples", "Greys")
i <- 1 # This is to be replaced by for loop
x <- subset(GNI2010, continent == cont[i], cex = 5)
# create treemap
layout(matrix(1:6, 3, byrow = TRUE), widths = widths, heights = heights)
x1 <- tmPlot(x,
index=c("iso3"),
vSize="population",
vColor="GNI",
type="value", title = "",
position.legend = "none",
palette = palettes[i])
grid.text(cont[i], 0.5, 0.5, gp=gpar(fontsize=20, font = 2, col = "white"))
## x1 is does not make a plot as such and tmPlot overwrites layout
I understand that my solution to scale the plots based on GNI sum is not right. I might make another question about that later, once I figure out how to plot these treemaps in a same window.
EDIT: I think the answer to this question is "no". Currently you cannot save grid.newpage objects by name, neither can you save several of these on a page, because the function "erases the current device or moves to a new page" as said in the description. However, it is possible to find work arounds. tmPlot package does not currently (as of 23 March, 2013) support viewports, but the development version does.
Thanks for your question. The output of tmPlot is indeed not a saved plot.
In the next update I will add argument vp, by which a viewport can be specified to draw in. Only if it is not specified, grid.newpage is called.
UPDATE: You could check and test the development version at https://github.com/mtennekes/treemap
To apply the example of Bryan Hanson:
vplayout <- function(x, y) viewport(layout.pos.row = x, layout.pos.col = y)
grid.newpage()
pushViewport(viewport(layout = grid.layout(1, 2)))
tmPlot(GNI2010,
index="continent",
vSize="population",
vColor="GNI",
type="value",
vp = vplayout(1,1))
tmPlot(GNI2010,
index=c("continent", "iso3"),
vSize="population",
vColor="GNI",
type="value",
vp = vplayout(1,2))
Here's an approach that is very flexible for any grid graphics:
vplayout <- function(x, y) viewport(layout.pos.row = x, layout.pos.col = y)
grid.newpage()
pushViewport(viewport(layout = grid.layout(1, 2)))
print(a, vp = vplayout(1,1))
print(b, vp = vplayout(1,2))
Where a and b are your saved plot objects. So test each plot individually ahead of time, save them as a, b, ... then plot them as above.
Oh, and if tmPlot always does grid.newpage then check to see if it has a has new.page argument which you can set to FALSE, or make a copy of the function and comment out the newpage.