grid, gridBase: How to plot in different regions? - grid

I would like to create R plots within four regions with a layout specified by grid. Furthermore, gridBase should be used to actually plot in the regions using base graphics. Below is an example. The problem is that the 4 plots are not plotted in the four different regions, they are rather overlaid.
require(grid)
require(gridBase)
## set up the grid layout
gl <- grid.layout(2, 2, widths=unit(c(3, 3), "inches"),
heights=unit(c(3, 3), "inches"))
pushViewport(viewport(layout=gl)) # use this layout in a viewport
## plot
par. <- par(no.readonly=TRUE) # save plot settings
vps <- baseViewports() # create list of grid viewports corresponding to inner, figure, and plot regions
pushViewport(vps$inner, vps$figure, vps$plot)
for(i in 1:2) { # rows
for(j in 1:2) { # columns
pushViewport(viewport(layout.pos.row=i, layout.pos.col=j))
par(plt=gridPLT(), new=TRUE)
x <- 1:10
plot(x, x+runif(10), type="b")
upViewport()
}
}
par(par.) # reset plot settings

This seems to work:
require(grid)
require(gridBase)
gl <- grid.layout(2, 2, widths=unit(c(3, 3), "inches"),
heights=unit(c(3, 3), "inches"))
pushViewport(viewport(layout=gl))
par. <- par(no.readonly=TRUE)
for(i in 1:2) {
for(j in 1:2) {
pushViewport(viewport(layout.pos.row=i, layout.pos.col=j))
par(plt=gridPLT())
x <- 1:10
plot(x, x+runif(10), type="b", xlab="", ylab="")
par(new=TRUE) # to be run after first plot
upViewport()
}
}
par(par.)

Related

Icons as x-axis labels in R

I would like to plot something like this (from this paper) where icons, in this case small graphs, are used as tick labels.
I get this far, where icons are more or less properly placed:
This is the code:
library(igraph)
npoints <- 15
y <- rexp(npoints)
x <- seq(npoints)
par(fig=c(0.05,1,0.3,1), new=FALSE)
plot(y, xlab=NA, xaxt='n', pch=15, cex=2, col="red")
lines(y, col='red', lwd=2)
xspan <- 0.9
xoffset <- (0.07+0.5/npoints)*xspan
for(i in 1:npoints){
x1 <- (xoffset+(i-1)/npoints)*xspan
x2 <- min(xspan*(xoffset+(i)/npoints),1)
par(fig=c(x1,x2,0,0.5), new=TRUE)
plot(graph.ring(i), vertex.label=NA)
}
However, if the number of points grows (e.g. npoints <- 15) it complains because there is no place for the icons:
Error in plot.new() : figure margins too large
I wonder wether there is a more natural way to do this so that it works for any (reasonable) number of points.
Any advice is welcome.
library(igraph)
npoints <- 15
y <- rexp(npoints)
x <- seq(npoints)
# reserve some extra space on bottom margin (outer margin)
par(oma=c(3,0,0,0))
plot(y, xlab=NA, xaxt='n', pch=15, cex=2, col="red")
lines(y, col='red', lwd=2)
# graph numbers
x = 1:npoints
# add offset to first graph for centering
x[1] = x[1] + 0.4
x1 = grconvertX(x=x-0.4, from = 'user', to = 'ndc')
x2 = grconvertX(x=x+0.4, from = 'user', to = 'ndc')
# abline(v=1:npoints, xpd=NA)
for(i in x){
print(paste(i, x1[i], x2[i], sep='; '))
# remove plot margins (mar) around igraphs, so they appear bigger and
# `figure margins too large' error is avoided
par(fig=c(x1[i],x2[i],0,0.2), new=TRUE, mar=c(0,0,0,0))
plot(graph.ring(i), vertex.label=NA)
# uncomment to draw box around plot to verify proper alignment:
# box()
}

Texture in barplot for 7 bars in R?

I have 7 different categories per each value in X. I am using barplot to plot these categories. Such graph looks fine in colors printer, but what if I want it to be fine in black & white. You can check the graph below. I want to have different colors texture, so the graph looks good in color and black & white printer.
I used densities = c(10,30,40,50,100,60,80) for density parameter in barplot function. Are there any other ways to do different texture in barplot?
Note: I tried the angle value in barplot. However, it isn't a good solution in that case, since not all bars have high values (i.e height of the bar).
Along the lines of my comment, you might find the following helpful:
# data generation ---------------------------------------------------------
set.seed(1)
mat <- matrix(runif(4*7, min=0, max=10), 7, 4)
rownames(mat) <- 1:7
colnames(mat) <- LETTERS[1:4]
# plotting settings -------------------------------------------------------
ylim <- range(mat)*c(1,1.5)
angle1 <- rep(c(45,45,135), length.out=7)
angle2 <- rep(c(45,135,135), length.out=7)
density1 <- seq(5,35,length.out=7)
density2 <- seq(5,35,length.out=7)
col <- 1 # rainbow(7)
# plot --------------------------------------------------------------------
op <- par(mar=c(3,3,1,1))
barplot(mat, beside=TRUE, ylim=ylim, col=col, angle=angle1, density=density1)
barplot(mat, add=TRUE, beside=TRUE, ylim=ylim, col=col, angle=angle2, density=density2)
legend("top", legend=1:7, ncol=7, fill=TRUE, col=col, angle=angle1, density=density1)
par(bg="transparent")
legend("top", legend=1:7, ncol=7, fill=TRUE, col=col, angle=angle2, density=density2)
par(op)

How to draw a simple grid/grill in the background using the R package 'grid'?

I would like to construct a grid object for a background behind a base graphics plot. When drawn, it should simply colorize the background in gray and put a red grid/grill on this background. The following shows a trial, but the grid obviously doesn't see the correct 'window'/viewport... How can this be achieved?
require(grid)
require(gridBase)
## function to compute a "background grob" (gray background with red 'grid')
bgGrob <- function(v, h, col, fill, default.units="npc", vp=NULL)
{
## background
br <- rectGrob(gp=gpar(col=NA, fill=fill), vp=vp) # background rectangle
## grid: construct grobs
vl <- segmentsGrob(x0=v, y0=0, x1=v, y1=1, # vertical lines
default.units=default.units, gp=gpar(col=col), vp=vp)
hl <- segmentsGrob(x0=0, y0=h, x1=1, y1=h, # horizontal lines
default.units=default.units, gp=gpar(col=col), vp=vp)
## grid: pack grobs
fg <- frameGrob(vp=vp) # set up basic frame grob (for packing)
u1 <- unit(1, units=default.units)
fg <- packGrob(fg, br, col=1, row=1, # background rectangle
width=u1, height=u1, force.width=TRUE)
fg <- packGrob(fg, vl, col=1, row=1, # vertical lines
width=u1, height=u1, force.width=TRUE)
fg <- packGrob(fg, hl, col=1, row=1, # horizontal lines
width=u1, height=u1, force.width=TRUE)
fg
}
## data
x <- 1:10
y <- rev(x)
## layout
grid.newpage()
gl <- grid.layout(nrow=1, ncol=1, widths=0.8, heights=0.8,
default.units="npc")
pushViewport(viewport(layout=gl))
vp <- viewport(layout.pos.row=1, layout.pos.col=1)
pushViewport(vp)
par(plt=gridPLT())
par(new=TRUE)
## set up coordinate system
plot.window(range(x), range(y), log="y")
v <- axTicks(1, axp=par("xaxp"), log=par("xlog")) # x values of vertical lines (2, 4, 6, 8, 10)
h <- axTicks(2, axp=par("yaxp"), log=par("ylog")) # y values of horizontal lines (1, 2, 5, 10)
## => correct values
## background
## trial 1
grid.draw(bgGrob(v=v, h=h, col="red", fill="gray90", default.units="native",
vp=grid::dataViewport(x, y)))
## trial 2
## grid.draw(bgGrob(v=v, h=h, col="red", fill="gray90", default.units="native"))
## trial 3
## grid.draw(bgGrob(v=v, h=h, col="red", fill="gray90", vp=vp, default.units="native"))
## plot
plot(x, y, type="b", log="y")
popViewport()
UPDATE
Based on Baptiste's first answer, here is a more complete minimal example (with 'Q' addressing follow-up questions):
require(grid)
require(gridBase)
bgGrob <- function(v, h, gp=gpar(fill="grey90", col="red"), vp=NULL)
grobTree(rectGrob(),
segmentsGrob(v, unit(0, "npc"), v, unit(1, "npc")),
segmentsGrob(unit(0, "npc"), h, unit(1, "npc"), h),
vp=vp, gp=gp)
## data
x <- 1:10
y <- rev(x)
## layout, par (for using base graphics)
plot.new()
gl <- grid.layout(nrow=1, ncol=1, widths=0.8, heights=0.8,
default.units="npc")
pushViewport(viewport(layout=gl))
vp <- viewport(layout.pos.row=1, layout.pos.col=1)
pushViewport(vp)
par(plt=gridPLT(), new=TRUE)
## set up coordinate system
plot.window(range(x), range(y), log="y")
## get tick locations
v <- axTicks(1, axp=par("xaxp"), log=par("xlog")) # x values of vertical lines
h <- axTicks(2, axp=par("yaxp"), log=par("ylog")) # y values of horizontal lines
## draw background
grid.draw(bgGrob(v=v, h=h, vp=viewport(width=1, height=1))) # Q: where are the red grill lines?
## draw base graphics on top of the background
plot(x, y, type="b", log="y")
## (check +) finalize
grid.rect(gp=gpar("blue")) # Q: why is nothing drawn?
popViewport()
I initially thought you needed baseViewports(), but it looks like par("usr") gives you enough information to set up a grid viewport with the coordinates system corresponding to the axes. Note that log scales require extra care. I still think this is a bad idea; it will probably break as soon as you place non-trivial base graphics. One is usually much better off not mixing the two systems.
require(grid)
require(gridBase)
bgGrob <- function(v, h, gp=gpar(fill="grey90", col="red"), vp=NULL, def="native")
grobTree(rectGrob(),
segmentsGrob(v, unit(0, "npc"), v, unit(1, "npc"), def=def),
segmentsGrob(unit(0, "npc"), h, unit(1, "npc"), h, def=def),
vp=vp, gp=gp)
grid.bg = function(...)
grid.draw(bgGrob(...))
## data
x <- 1:10
y <- rev(x)
## layout, par (for using base graphics)
grid.newpage()
plot.new()
gl <- grid.layout(nrow=1, ncol=1, widths=0.8, heights=0.8,
default.units="npc")
pushViewport(viewport(layout=gl))
vp <- viewport(layout.pos.row=1, layout.pos.col=1)
pushViewport(vp)
par(plt=gridPLT(), new=TRUE)
## set up coordinate system
plot.window(range(x), range(y), log="y")
# suppressWarnings(base <- baseViewports())
## get tick locations
v <- axTicks(1, axp=par("xaxp"), log=par("xlog")) # x values of vertical lines
h <- axTicks(2, axp=par("yaxp"), log=par("ylog")) # y values of horizontal lines
if(par("xlog")) v <- log10(v)
if(par("ylog")) h <- log10(h)
usr <- par("usr")
## draw background
grid.bg(v=v, h=h, vp=viewport(xscale=usr[1:2], yscale=usr[3:4]))
## draw base graphics on top of the background
plot(x, y, type="b", log="y")

Overlay histogram on top and right of a plot

I am trying to create a plot similar to the one asked here:
Rotate histogram in R or overlay a density in a barplot
I am replacing the scatterplot with heatmap keeping everything else same without any luck. I tried to modify different parameters but it is not helping.
scatterBarNorm <- function(x, dcol="blue", lhist=20, num.dnorm=5*lhist, ...){
## check input
stopifnot(ncol(x)==2)
## set up layout and graphical parameters
layMat <- matrix(c(2,0,1,3), ncol=2, byrow=TRUE)
layout(layMat, widths=c(5/7, 2/7), heights=c(2/7, 5/7))
ospc <- 0.5 # outer space
pext <- 4 # par extension down and to the left
bspc <- 1 # space between scatter plot and bar plots
par. <- par(mar=c(pext, pext, bspc, bspc),
oma=rep(ospc, 4)) # plot parameters
## scatter plot (removed this)
#plot(x, xlim=range(x[,1]), ylim=range(x[,2]), ...)
##added this
heatmap(x, Rowv=NA, Colv=NA)
## 3) determine barplot and height parameter
## histogram (for barplot-ting the density)
xhist <- hist(x[,1], plot=FALSE, breaks=seq(from=min(x[,1]), to=max(x[,1]),
length.out=lhist))
yhist <- hist(x[,2], plot=FALSE, breaks=seq(from=min(x[,2]), to=max(x[,2]),
length.out=lhist)) # note: this uses probability=TRUE
## determine the plot range and all the things needed for the barplots and lines
xx <- seq(min(x[,1]), max(x[,1]), length.out=num.dnorm) # evaluation points for the overlaid density
xy <- dnorm(xx, mean=mean(x[,1]), sd=sd(x[,1])) # density points
yx <- seq(min(x[,2]), max(x[,2]), length.out=num.dnorm)
yy <- dnorm(yx, mean=mean(x[,2]), sd=sd(x[,2]))
## barplot and line for x (top)
par(mar=c(0, pext, 0, 0))
barplot(xhist$density, axes=FALSE, ylim=c(0, max(xhist$density, xy)),
space=0) # barplot
lines(seq(from=0, to=lhist-1, length.out=num.dnorm), xy, col=dcol) # line
## barplot and line for y (right)
par(mar=c(pext, 0, 0, 0))
barplot(yhist$density, axes=FALSE, xlim=c(0, max(yhist$density, yy)),
space=0, horiz=TRUE) # barplot
lines(yy, seq(from=0, to=lhist-1, length.out=num.dnorm), col=dcol) # line
## restore parameters
par(par.)
}
require(mvtnorm)
X <- abs(rmvnorm(1000, c(0,0), matrix(c(1, 0.8, 0.8, 1), 2, 2)))
scatterBarNorm(X, xlab=expression(italic(X[1])), ylab=expression(italic(X[2])))

Achieving multiple plots with different plotting widths in R

I want to create multiple plots in a single plotting window in which the width of the panels of each plot are proportional to xlim of each plot.
At the moment I use:
layout(matrix(c(1:8,10,9), 5, 2, byrow = FALSE), widths=2)
layout.show(10)
Basically, I would like width to be applied individually to each plot rather than to all the plots in a column. What is the best way of doing this?
layout only works if the plots can be arranged over a regular grid,
but they need not have the same widths.
layout(
matrix(
c(1,1,2,3,3,2,4,5,5,6,6,6),
nc=3, byrow = TRUE
)
)
layout.show(6)
If you want something really irregular, you can use par(fig=...,new=TRUE).
plot.new()
par(mar=c(2,2,1,1))
k <- 4
f <- function()
plot(rnorm(20),rnorm(20), xlab="", ylab="", main="", las=1)
for(i in 1:k) {
par(fig=c(0,i/(k+1), (i-1)/k, i/k), new=TRUE)
f()
par(fig=c(i/(k+1),1, (i-1)/k, i/k), new=TRUE)
f()
}

Resources