Search code examples
rr-grid

How to create a pairs-plot (matrix-like plot) with `grid`?


I'm trying my first steps purely in grid. As an exercise, I would like to create a pairs plot (similar to pairs()) purely based on grid. The function myplotGrob below should create the grid object (grob; or gTree) and return the object.

I'm not sure what's the best way to continue. Which units should one use? (tried "null", too) Is frameGrob meant to set up the layout? (this is what I understood from Paul Murrell's book) How do I have to choose/adjust the viewports such that I get the desired plot (so far, I only see a mess) Is the layout meant to be set up beforehand or is it better to just step-by-step "concatenate" additional panels to get the (4, 4) plot matrix?

require(grid)
require(mvtnorm)

set.seed(271)
X <- rmvnorm(1000, mean=1:4, sigma=diag(4:1)) # goal: draw this in a pairs plot

## auxiliary function
panel <- function(x, y) pointsGrob(x=x, y=y, name="panel", gp=gpar(), vp=NULL)

## creates and returns a gTree (class)
myplotGrob <- function(X, name=NULL, gp=NULL, vp=NULL)
{
    ## x-axis grob

    ## y-axis grob

    ## ...

    ## set up layout
    layout <- grid.layout(4, 4, # (4, 4) matrix
                          widths=rep(0.25, 4), heights=rep(0.25, 4),
                          default.units="npc")
    ## pushViewport(viewport(layout=layout)) # required???

    all <- frameGrob(layout=layout) # produces a gTree without children
    for(i in 1:4) {
        for(j in 1:4) {
            ## group grobs together
            gt <- gTree(X,
                        children=gList(panel(X[,i], X[,j])),
                        name=name, gp=gp, vp=vp, cl="myplotGrob")
            all <- placeGrob(all, gt, row=i, col=j)
        }
    }
    all
}

## draw the gTree
grid.myplot <- function(...) grid.draw(myplotGrob(...))

## call
grid.myplot(X)

UPDATE

As it was asked for, here is the design/layout of the original problem I have in mind (the above would have only been a minimal/learning example). The units in cm were just for me (they should be 'relative' in the end). Of course, the number of panels may vary. I would like all parts to be grid objects, so that the function which creates the graphic will return an object (without printing/drawing). This way, each part can be modified afterwards. The graphic should display results from an array of dimension 5 (or less): one dimension is displayed in the row panels [row.vars], one in the column panels [col.vars], one on the x axis of each panel [xvar], and each panel can contain 2 different dimensions of the array (differing by color and line type) [I used d and n in the drawing]. If course, if the array is four-dimensional, then row 8 of the above design should be missing. I can construct the layout via grid, but the whole question is how to continue from there. That's what I wanted to express with my "minimal example" above.

enter image description here


Solution

  • I think you can divide the task in two main parts, like the basic examples in grid.panel() and grid.multipanel()

    1- build a function that will produce a single panel, returned as a gTree. You need to figure out all the parameters, i.e. limits, axes, colours, shapes, grid, coordinates, ... You might end up rewriting lattice panel functions and axes,

    grid.newpage()
    grid::grid.panel(vp=viewport(width=0.8, height=0.8))
    

    enter image description here

    2- assemble the panels in a layout. This is much easier (and cleaner) with gtable,

    library(gtable)
    
    grid.newpage()
    lg <- replicate(16, grobTree(rectGrob(), pointsGrob()), simplify=FALSE)
    
    gt <- gtable_matrix("pairs", grobs=matrix(lg, ncol=4),
                        widths=unit(rep(1, 4), "null"),
                        heights=unit(rep(1, 4), "null"))
    
    gt <- gtable_add_col_space(gt, width=unit(0.5,"line"))
    gt <- gtable_add_row_space(gt, height=unit(0.5,"line"))
    gt <- gtable_add_padding(gt, padding=unit(1,"line"))
    
    grid.draw(gt)
    

    If you want to build everything from scratch, here too you'll end up having to reinvent a good portion of gtable, I reckon.

    enter image description here