Marius Hofert
2012-Oct-19 10:18 UTC
[R] grid(Base): How to avoid "Figure region too small and/or viewport too large" by specifying 'relative' units?
Dear grid-expeRts,
The goal:
I would like to construct a plot (matrix) with grid and gridBase,
which consists of four "sub-plots". The sub-plots should have a square
plotting
region as one would force with par(pty="s") in base graphics.
The problem:
I don't get a square plotting region, not even by specifying
pty="s" in par(). Indeed, if you display the grid layout by commenting
in
"grid.show.layout(gl)" below, you'll see that the grid layout is
fine, but the
plot does not seem to respect the layout measurements.
The initial quick-and-dirty hack:
Use absolute measurements, i.e., specify all
units in inches. This worked perfectly fine for me initially. However, when the
device width and height are not set accordingly (and one can never know what
others specify here -- indeed the problem arose this way), this produces errors
of type "Error in gridPLT() : Figure region too small and/or viewport too
large".
I also found this issue in this post
(https://stat.ethz.ch/pipermail/r-help/2008-December/181993.html), but I am
wondering what's the correct approach towards this problem / how can one
specify
"relative" units but guarantee that the grid layout is respected when
plotting
is done? [We use this plot in a quite complicated setup and the quick solution
to specify inches and a large enough width/height for the device fails.]
Cheers,
Marius
require(grid)
require(gridBase)
## setup
strg <- LETTERS[1:2] # row variables
t <- c(0.2, 0.8) # column variables
## plot variables (spaces)
pspc <- c(3,3)
spc <- c(0.3, 0.3)
axlabspc <- c(1.2, 0.75)
labspc <- c(0.3, 0.3)
## save plot settings
par. <- par(no.readonly=TRUE)
## set up the grid layout
nx <- 2 # number of sub-plots per column
nx. <- 5 # number of grid rectangles per column
ny <- 2 # number of sub-plots per row
ny. <- 5 # number of grid rectangles per row
plot.new() # start (empty) new page with 'graphics'
gl <- grid.layout(nx., ny., # "2 x 2" grid together with spaces
=> 5 x 5 grid
widths=c(axlabspc[1], rep(c(pspc[1], spc[1]), nx-1), pspc[1],
labspc[1]),
heights=c(labspc[2], rep(c(pspc[2], spc[2]), ny-1), pspc[2],
axlabspc[2]))
## grid.show.layout(gl) # display the layout; use this to see that the sub-plots
do not match the layout specification
pushViewport(viewport(layout=gl)) # use this layout in a viewport
## generate dummy data to plot in the four sub-plots
n <- 10
x <- array(NA, dim=c(nx, ny, n))
for(i in 1:2) for(j in 1:2) x[i,j,] <- rep(i,n)+rep(j,n)+runif(n)
## go through the "panels"
set.seed(1)
for(i in 1:nx) { # rows
i. <- 2*i # column index in layout (for jumping over gaps)
yran <- range(x[i,,]) # for forcing the same y-axis within a row
for(j in 1:ny) { # columns
j. <- 2*j # row index in layout (for jumping over gaps)
pushViewport(viewport(layout.pos.row=i., layout.pos.col=j.))
## plot
par(plt=gridPLT()) # start a 'graphics' plot
par(new=TRUE) # always do this before each new 'graphics' plot
plot(range(1:n), yran, type="n", ann=FALSE, axes=FALSE) # set
up coordinate axes
points(1:n, x[i,j,], type="b") # actual plot
grid.rect()
## axes
if(i==nx) axis(1) # x axis
if(j==1) axis(2) # y axes
upViewport()
## column labels
if(i==1){
pushViewport(viewport(layout.pos.row=1, layout.pos.col=j.))
grid.rect()
grid.text(t[j], x=0.5, y=0.5)
upViewport()
}
## row labels
if(j==2){
pushViewport(viewport(layout.pos.row=i., layout.pos.col=nx.))
grid.rect()
grid.text(strg[i], x=0.5, y=0.5, rot=-90)
upViewport()
}
}
}
## x axis label
pushViewport(viewport(layout.pos.row=ny., layout.pos.col=2:(ny.-1)))
grid.text(expression(gamma), y=unit(0.5, "null"))
upViewport()
## y axis label
pushViewport(viewport(layout.pos.row=2:(nx.-1), layout.pos.col=1))
grid.text(expression(f[gamma]), rot=90, x=unit(0.5, "null"))
upViewport()
## restore plot settings
par(par.)
Marius Hofert
2012-Oct-20 06:10 UTC
[R] grid(Base): How to avoid "Figure region too small and/or viewport too large" by specifying 'relative' units?
In the meanwhile, I found a more minimal example which shows the problem (just
change 'inch' to TRUE to see the difference):
require(grid)
inch <- FALSE # TRUE
d <- if(inch) 5 else 1
pspc <- d*c(0.3, 0.3) # width, height of panels
spc <- d*c(0.05, 0.05) # width, height of space
axlabspc <- d*c(0.1, 0.1) # width y label, height x label
labspc <- d*c(0.05, 0.05) # width label boxes, height label boxes
par. <- par(no.readonly=TRUE)
gl <- grid.layout(5, 5, default.units=if(inch) "inches" else
"npc",
widths=c(axlabspc[1], pspc[1], spc[1], pspc[1], labspc[1]),
heights=c(labspc[2], pspc[2], spc[2], pspc[2], axlabspc[2]))
grid.show.layout(gl)
pushViewport(viewport(layout=gl))
for(i in 1:2) {
for(j in 1:2) {
pushViewport(viewport(layout.pos.row=2*i, layout.pos.col=2*j,
name="foo"))
grid.rect()
upViewport()
}
}
par(par.)