Stochastic Nonsense

Put something smart here.

Plotting in Grids

This is post #12 in a running series about plotting in R.

I regularly find myself wanting to show arrays or grids of plots in R. This is straightforward using par and mfrow as long as you want a symmetric, evenly spaced grid of plots. Unfortunately, this often is not what I want. Even more unfortunately, this is a hard question to google for. I’ve tried array of plots, grid of plots, matrix of plots, asymmetric grids of plots, asymmetric arrays, uneven grids of plots, uneven mfrow, uneven mfcol, etc, and nothing worked. (Searches listed here in the hopes that other people with the same question will find the answer.)

I actually didn’t think this could be accomplished without using lattice and ggplot2, but I recently discovered that it can be done with R’s base plotting functions. The function layout provides what we’re looking for. It takes a matrix describing where you want your sequence of plots to go. After creating your layout, you can use layout.show to visually see where your plots will go. Let’s take a look at some examples.

This creates a two by two grid, exactly as mfrow does.

1
2
3
# 2 by 2 grid, the same as mfrow=c(2,2)
pp <- layout(matrix(c(1,2,3,4), 2, 2, byrow=T))
layout.show(pp)

For comparison, this creates a 2 by 2 grid as mfcol does. The only difference is the order of the plot numbers in the matrix.

1
2
3
# 2 by 2 grid, the same as mfcol=c(2,2)
pp <- layout(matrix(c(1,2,3,4), 2, 2, byrow=F))
layout.show(pp)

We can put 0 in any position in the matrix to not plot there.

1
2
3
# no plotting in the first quadrant
pp <- layout(matrix(c(1,0,2,3), 2, 2, byrow=T))
layout.show(pp)

Now, let’s just have one plot use all of the left column. The trick to spanning columns like this is to repeat the number of the plot that you want to span — note that 1 occurs twice in the layout matrix.

1
2
3
# now a fat plot on the left and two small plots in the right column
pp <- layout(matrix(c(1, 1, 2, 3), 2, 2, byrow=F))
layout.show(pp)

Finally, we can set widths for the columns (or for the rows — just use heights instead of widths).

1
2
3
# same as above, but with the left column having 3/4 of the width
pp <- layout(matrix(c(1, 1, 2, 3), 2, 2, byrow=F), widths=c(3,1))
layout.show(pp)

Now, let’s show off what I originally wanted to do: display a plot of two dimensions of a distribution, along with the marginal distributions. I’m wrapping the functionality up into a function so it’s easy to reuse. I use plot to show the sample and barplot to show the distribution as calculated by hist.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# now lets demonstrate with a plot of the multivariate normal and histograms of the marginal distributions
# use package MASS to get the mvrnorm function

plotWithMarginals <- function(x, y){

# find min / max on each dimension
# then set up breaks so that even if x, y are on very different ranges things work
mm <- max(abs(range(x, y)))
breaks <- seq(-mm, mm, by=(2*mm)/1000)

hist0 <- hist(x, breaks=breaks, plot=F)
hist1 <- hist(y, breaks=breaks, plot=F)

# create a grid and check it out to make sure that it's what we want
pp <- layout(matrix(c(2,0,1,3), 2, 2, byrow=T), c(3,1), c(1,3), T)
layout.show(pp)

rang <- c(-mm, mm)

par(mar=c(3,3,1,1))
plot(x, y, xlim=rang, ylim=rang, xlab='', ylab='')

# now plot marginals
top <- max(hist0$counts, hist1$counts)
par(mar=c(0,3,1,1))
barplot(hist0$counts, axes=F, ylim=c(0, top), space=0)

par(mar=c(3,0,1,1))
barplot(hist1$counts, axes=F, xlim=c(0,top), space=0, horiz=T)
}

# mvrnorm <-- sample from a multivariate normal distn
library(MASS)

Now that all the prep is done, this shows a multivariate normal distribution with no correlation between the two variables. Note the shape of the marginal distributions.

1
2
3
eye2 <- matrix(c(1,0,0,1), 2, 2)
sample <- mvrnorm(n=10000, mu=c(0,0), Sigma=eye2)
plotWithMarginals(sample[,1], sample[,2])

plot12.05

And finally, for contrast, a correlated multivariate normal.

1
2
3
yescorr <- matrix(c(1, 0.9, 0.9, 1), 2, 2, byrow=T)
sample <- mvrnorm(n=10000, mu=c(0,0), Sigma=yescorr)
plotWithMarginals(sample[,1], sample[,2])