Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested layouts in R

Tags:

plot

r

I want to make a plot consisting of multiple plots consisting of multiple plots, say a 5x2 grid with three plots in each cell. To be more precise, what I need is not just one figure but finding a way of using my plotting function multiple times in a single plot.

I have written a function that uses layout to stack plots, with a common axis in outer margin. I actually need it for seqIplot and seqdplot functions from the TraMineR package, but as far as I understand the problem is not related to those, so here is a minimal working example with barplot.

stackedplot <- function(data){
  layout(matrix(c(1:3), nrow=3))
    par(mar=c(0,0,0,0), oma=c(4,1,1,1), mgp=c(3,0.5,0), cex=1)
    barplot(data[[1]], axes=F, xlab="", ylab="", horiz=TRUE)
    barplot(data[[2]], axes=F, xlab="", ylab="", horiz=TRUE)
    barplot(data[[3]], axes=F, xlab="", ylab="", horiz=TRUE)
    axis(1, at=c(0:10)/10, outer=TRUE)
    mtext("Label", line=2, side=1)
}
stackedplot(list(1:10, 10:1, rep(1,10)))

What I would like to do is to then use something like layout again and use stackedplot for the grids of the layout, i.e. something like this (which, of course, does not work):

layout(matrix(c(1:2), nrow=1))
stackedplot(list(1:10, 10:1, rep(1,10)))
stackedplot(list(rep(1,10), 1:10, 10:1))

I have tried split.screen, with no success:

split.screen(c(1,2))
screen(1)
stackedplot(list(1:10, 10:1, rep(1,10)))
screen(2)
stackedplot(list(rep(1,10), 1:10, 10:1))
close.screen(all = TRUE)

I also tried grid package, but apparently it is not compatible with base graphics.

grid.newpage()
pushViewport(viewport(x=0, y=0, width=0.5, height=1, 
                          default.units="native"))
print(stackedplot(list(1:10, 10:1, rep(1,10))), newpage=FALSE)
pushViewport(viewport(x=0.5, y=0, width=0.5, height=1,
                      default.units="native"))
print(stackedplot(list(rep(1,10), 1:10, 10:1)), newpage=FALSE)
like image 666
Satu Avatar asked Oct 19 '22 22:10

Satu


1 Answers

After more research and some help I am now answering my own question in case it would be useful to someone else.

Nested layouts can be created with the grid package, which can be used for base graphics using the gridBase package. The function for the stacked plots is written as follows.

library(grid)
library(gridBase)

stackedplot <- function(data, main=""){

  top.vp <- viewport(layout=grid.layout(nrow=5, ncol=1, 
                                        heights=unit(c(3, 1, 1, 1, 5), 
                                                     c("lines", "null", "null", "null", "lines"))),
                     width=unit(0.9, "npc"))

  title <- viewport(layout.pos.row=1, layout.pos.col=1, name="title")
  p1 <- viewport(layout.pos.row=2, layout.pos.col=1, name="plot1")
  p2 <- viewport(layout.pos.row=3, layout.pos.col=1, name="plot2")
  p3 <- viewport(layout.pos.row=4, layout.pos.col=1, name="plot3")
  xaxis <- viewport(layout.pos.row=5, layout.pos.col=1, name="xaxis")

  splot <- vpTree(top.vp, vpList(title, p1, p2, p3, xaxis)) # Defining the hierarchy of the viewports
  pushViewport(splot) # Creating viewports for plotting with the definitions of splot

  upViewport() # Navigating up in the viewport tree
  downViewport("plot1") # Navigating down in the viewport tree, searching for viewport "plot1"
  grid.rect() # Plotting a rectangle (borders for the viewport)
  par(plt=gridPLT(), new=TRUE) # Taking the dimensions of the viewport for a base graphics plot
                               # Adding plot to an existing plot
  barplot(data[[1]], axes=FALSE, xlab="", ylab="", horiz=TRUE)

  upViewport()
  downViewport("plot2")
  grid.rect()
  par(plt=gridPLT(), new=TRUE)
  barplot(data[[2]], axes=FALSE, xlab="", ylab="", horiz=TRUE)

  upViewport()
  downViewport("plot3")
  grid.rect()
  par(plt=gridPLT(), new=TRUE)
  barplot(data[[3]], xlab="", ylab="", horiz=TRUE)

  upViewport()
  downViewport("xaxis")
  grid.text("X label", y = unit(2, "lines"))

  upViewport()
  downViewport("title")
  grid.text(main, y = unit(1, "lines"))

  upViewport(2)
}

The function first describes a viewport (of 90% of the window's width) that is divided into a 5x1 grid of viewports with differing heights. Each viewport in the grid is given a name that can be later called. The tree of viewports (splot) is described with vpTree which defines the hierarchical structure of the viewports. After describing the viewports those are actually prepared for plotting with pushViewport.

Now each named viewport is first seeked and then opened for plotting with upViewport (which goes up in the viewport tree) and downViewport (which seeks for the requested viewport down in the viewport tree).

For plotting base graphics, gridPLT is needed here (alternatively gridFIG or gridOMI can be used, see the manual of gridBase for further info). After that any base graphics function can be used to plot into the current viewport.

After the requested plots, upViewport(2) is used to navigate back to the root (2 viewports up in the hierarchy).

Now the stackedplot function can be called multiple times in another grid as follows.

opar <- par(no.readonly=TRUE) # Saving graphical parameters
plot.new() # Needed for par(new=TRUE) in stackedplot()

multitop.vp <- viewport(layout=grid.layout(1,2), width = unit(0.95, "npc"))
pl1 <- viewport(layout.pos.col=1, layout.pos.row=1, name="A")
pl2 <- viewport(layout.pos.col=2, layout.pos.row=1, name="B")
vpall <- vpTree(multitop.vp, vpList(pl1,pl2))
pushViewport(vpall)

upViewport()
downViewport("A")
stackedplot(data=list(1:10,10:1,rep(10,10)),main="A")

upViewport()
downViewport("B")
stackedplot(data=list(10:1,rep(10,10),1:10),main="B")

upViewport(2)
par(opar) # Returning the graphical parameters saved earlier

enter image description here

like image 81
Satu Avatar answered Oct 22 '22 21:10

Satu