controlling the inner figure margin within grid.layout - r

I'm trying to plot multiple lattice plots in a grid.
To do so I'm using the following code:
plot <- xyplot(1:10~1:10)
page.layout <- grid.layout(nrow = 2, ncol = 1,
widths = unit(c(1), "null"),
heights = unit(c(1), "null"),
default.units = "null",
respect = FALSE,
just = "centre")
pushViewport(viewport(layout = page.layout))
pushViewport(viewport(layout.pos.row = 1))
par(mar = c(5, 4, 4, 2))
print(plot, newpage = FALSE)
popViewport()
pushViewport(viewport(layout.pos.row = 2))
par(mar = c(5, 4, 4, 2))
print(plot, newpage = FALSE)
popViewport()
I'd like now to reduce the space between the two figure, I read in the vignette for gridBase, that simple graphic controls, such has par(mar=c()) should be working, but it is not the case here.
Maybe I'm missing something obvious, but I can't figure out, why I can't control the margin parameters.
Any suggestions?

lattice provides some options to control the plot margins,
p <- xyplot(1:10~1:10,
par.settings=list(layout.heights=list(top.padding=-3, bottom.padding=-1)))
library(gridExtra)
grid.arrange(p, p, nrow=2)

Related

Fix text position in rasterVis::vectorplot

I'm trying to fix the position of some text in a rasterVis::vectorplot so that it stays in the same position even if I change the width and height of the png file.
I tried using the margin parameters of par but with no luck.
This is an example of what I got so far:
#Some raster data
proj <- CRS('+proj=longlat +datum=WGS84')
df <- expand.grid(x = seq(-2, 2, .01), y = seq(-2, 2, .01))
df$z <- with(df, (3*x^2 + y)*exp(-x^2-y^2))
r <- rasterFromXYZ(df, crs=proj)
#[A]
png("test01.png",width = 918,height = 850,res=100)
vectorplot(r,par.settings=list(layout.widths = list(axis.key.padding = 3)),
narrows = 500,length=0.1,lwd.arrows=0.4)
grid.text(substr(R.version.string, 1, 15),rot=90, x=0.92,y=0.14,gp = gpar(fontsize = 12, fontface = "italic"))
dev.off()
This is the output of [A]. That's how I want it:
Now, changing the width and height:
##[B]
png("test02.png",width = 1718,height = 850,res=100)
vectorplot(r,
par.settings=list(layout.widths = list(axis.key.padding = 3)),
narrows = 500,length=0.1,lwd.arrows=0.4)
grid.text(substr(R.version.string, 1, 15),rot=90, x=0.92,y=0.14,gp = gpar(fontsize = 12, fontface = "italic"))
dev.off()
This is the output of [B]:
As you can see the text doesn't stay in the same place. (I'm new with the rasterVis library.)
The layer function of the latticeExtra package with panel.text will help you here. The panel.text functions prints inside the area of the panel, so you have to add the option clip = list(panel = FALSE) to the settings list in order to print outside this area:
library(grid)
library(rasterVis)
vectorplot(r,par.settings=list(clip = list(panel = FALSE),
layout.widths = list(axis.key.padding = 3)),
narrows = 500,length=0.1,lwd.arrows=0.4) +
layer(panel.text(2.05, -2,
substr(R.version.string, 1, 15),
adj = c(0, 1),
srt = 90))

How to add multi-color text in R when working with viewports

I am trying to add multi color text in a title in one of our plots.
I have a grid layout that has one viewport, I am using gridtext as it makes it easy to add html and style the text, however I am not able to place it in the existing viewport.
Can you let me know how would I be able to do that?
titleFunction <- function() {
library(ggplot2)
library(gridExtra)
library(grid)
library(gridtext)
textSizeMajor = 36
textSizeMinor = 32
mainLO <- grid.layout(nrow = 4, ncol = 3,
widths = unit(c(textSizeMajor * 1.2, 1, .5),
c("points", "null", "cm")),
heights = unit(c(textSizeMajor, textSizeMajor, 40, 1),
c("points", "points", "points", "null")))
pushViewport(viewport(name="mainViewPort", layout = mainLO))
fontface = "bold"
# Main title
grid.text("Main Title",
vp = viewport(layout.pos.col = 2, layout.pos.row = 1),
gp=gpar(fontsize=26, fontface = fontface))
browser()
grid.draw(richtext_grob(text = '<span style="color:red">Hello</span>, <span style="color:blue">World</span>!'))
# Want to place this below Main title, where row = 2
# vp = viewport(layout.pos.col = 2, layout.pos.row = 2)
# main viewport pop
popViewport(1)
}
The main issue was dealing with the grid and textGrob objects. Here is the code that works
print(grid.draw(richtext_grob(text = subTitle,
gp = gpar(fontsize=22),
vp = viewport(layout.pos.col = 2, layout.pos.row = 2))) )

White space generated in grid.arrange

I need to arrange several plots for a figure. I am creating individual plots using base and grid graphics. In order to arrange them in a single figure I have been using grid.echo(), grid.grab() to convert to grobs and then arrangeGrob() and grid.arrange() to build the final figure. A few weeks ago my tentative figure was working fine but now when I rerun the code it produces a figure with whitespace in the margins of the plots.
I add a minimal example that shows the problem that I am facing...
##minimal example
library(grid)
library(gridExtra)
library(gridGraphics)
##test plot
plot_n1<-plot(1:10,1:10, asp=1)
##convert test plot to grob
grid.echo()
test_p<-grid.grab()
##simulate several plots arranged in a more complex layout
multi<-arrangeGrob(test_p, test_p, test_p, test_p, ncol=1, heights=c(1/4,1/4,1/4,1/4))
##create graph
png(filename="minimal_multiplot.png", res=300, width=20, height=20, units="cm")
grid.arrange(test_p, multi, ncol=2, widths=c(2/3,1/3))
dev.off()
What am I doing wrong?
There does indeed appear to be a problem when converting a graphics plot into a grid plot, then using grid.grab() to grab and then draw the plot into a smaller regions (i.e., using your method). For instance, using viewports to define a slightly smaller region (coloured grey in the image below), axis material is missing.
# Packages
library(grid)
library(gridGraphics)
plot(1:10,1:10)
grid.echo()
test_p = grid.grab()
grid.newpage()
pushViewport(viewport(x = 0, width = .85, just = "left"))
grid.rect(gp = gpar(col = NA, fill = "grey90"))
grid.draw(test_p)
upViewport()
grid.rect(gp = gpar(col = "grey90", size = .1, fill = NA))
But Paul Murrell (author of the gridGraphics package) offers an alternative (see the examples at ?gridGraphics::grid.echo, and pp. 156-157 of The gridGraphics package in The R Journal v7/1). One can define a function that draws the plot, then that function becomes the argument of grid.echo() at the time of drawing the plot within the viewport. newpage = FALSE stops grid from opening a new page. Note that none of the axis material is chopped off.
pf = function() {
plot(1:10,1:10)
}
grid.newpage()
pushViewport(viewport(x = 0, width = .85, just = "left"))
grid.rect(gp = gpar(col =NA, fill = "grey90"))
grid.echo(pf, newpage=FALSE)
upViewport()
grid.rect(gp = gpar(col = "grey90", size = .1, fill = NA))
So to get your desired plot, I would do something like this - but still using viewports.
pf = function() {
par(mar=c(7.2, 7.2, 1, 1), mex = .3, tcl = .15, mgp = c(3, .15, 0))
plot(1:10, 1:10, cex.axis = .75, cex.lab = .75)
}
grid.newpage()
pushViewport(viewport(layout = grid.layout(3, 2,
widths = unit(c(2, 1), "null"),
heights = unit(c(1, 1, 1), "null"))))
pushViewport(viewport(layout.pos.col = 1, layout.pos.row = 1:3))
grid.echo(pf, newpage = FALSE)
upViewport()
for(i in 1:3) {
pushViewport(viewport(layout.pos.col = 2, layout.pos.row = i))
grid.echo(pf, newpage = FALSE)
upViewport()
}
upViewport()
grid.rect(gp = gpar(col = "grey90", size = .1, fill = NA))

Center align bottom legend viewport or grob relative to plot area with grid package

I'm trying to align a legend at the bottom of a chart, centered respectively to that chart. But I'm having trouble aligning it.
The pictures below show the current rendering, where you can clearly see the legend is misaligned (red line for guidance).
library(grid)
draw <- function() {
masterLayout <- grid.layout(
nrow = 4,
ncol = 1,
heights = unit(c(0.1, 0.7, 0.1, 0.1), rep("null", 4)))
vp1 <- viewport(layout.pos.row=1, layout.pos.col = 1, name="title")
vp2 <- viewport(layout.pos.row=2, layout.pos.col = 1, name="plot")
vp3 <- viewport(layout.pos.row=3, layout.pos.col = 1, name="legend")
vp4 <- viewport(layout.pos.row=4, layout.pos.col = 1, name="caption")
pushViewport(
vpTree(viewport(layout = masterLayout, name = "master"),
vpList(vp1, vp2, vp3, vp4)))
## Draw main plot
seekViewport("plot")
pushViewport(viewport(width=unit(.8, "npc")))
grid.rect(gp=gpar("fill"="red")) # dummy chart
popViewport(2)
## Draw legend
seekViewport("legend")
colors <- list(first="red", second="green", third="blue")
data.names <- names(colors)
legend.cols <- length(data.names)
pushViewport(viewport(
width = unit(0.8, "npc"),
layout = grid.layout(ncol=legend.cols * 2,
nrow=1,
widths=unit(2.5, "cm"),
heights=unit(0.25, "npc"))))
idx <- 0
for(name in data.names) {
idx <- idx + 1
pushViewport(viewport(layout.pos.row=1, layout.pos.col=idx))
grid.circle(x=0, r=0.35, gp=gpar(fill=colors[[name]], col=NA))
popViewport()
idx <- idx + 1
pushViewport(viewport(layout.pos.row=1, layout.pos.col=idx))
grid.text(x=unit(-0.8, "npc"), "text", just="left")
popViewport()
}
popViewport(2)
}
draw()
I don't understand why you're doing so much with individual viewports. It makes it very complex. I would have thought it was much easier to have one viewport for the legend and then control the x coordinate of the text and circles relative to that. Something like this; I'm not sure it's exactly what you want but it feels it should be easy to control if you need to tweak it:
library(grid)
draw <- function() {
masterLayout <- grid.layout(
nrow = 4,
ncol = 1,
heights = unit(c(0.1, 0.7, 0.1, 0.1), rep("null", 4)))
vp1 <- viewport(layout.pos.row=1, layout.pos.col = 1, name="title")
vp2 <- viewport(layout.pos.row=2, layout.pos.col = 1, name="plot")
vp3 <- viewport(layout.pos.row=3, layout.pos.col = 1, name="legend")
vp4 <- viewport(layout.pos.row=4, layout.pos.col = 1, name="caption")
pushViewport(
vpTree(viewport(layout = masterLayout, name = "master"),
vpList(vp1, vp2, vp3, vp4)))
## Draw main plot
seekViewport("plot")
pushViewport(viewport(width=unit(.8, "npc")))
grid.rect(gp=gpar("fill"="red")) # dummy chart
popViewport(2)
## Draw legend
seekViewport("legend")
colors <- list(first="red", second="green", third="blue")
lab_centers <- seq(from = 0.2, to = 0.8, length = length(colors))
disp <- 0.03 # how far to left of centre circle is, and to right text is, in each label
for(i in 1:length(colors)){
grid.circle(x = lab_centers[i] - disp, r = 0.1, gp=gpar(fill = colors[[i]], col=NA))
grid.text("text", x = lab_centers[i] + disp)
}
popViewport(2)
}
draw()
grid.lines(c(0.5, 0.5), c(0, 1))
If your legend labels aren't all the same length, you probably need to left align them and tweak the way I've used a disp parameter but shouldn't be too hard.

Side-by-side Venn diagram using Vennerable

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")))

Resources