Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write vignette about grid #1239

Closed
hadley opened this issue Aug 4, 2015 · 16 comments
Closed

Write vignette about grid #1239

hadley opened this issue Aug 4, 2015 · 16 comments

Comments

@hadley
Copy link
Member

hadley commented Aug 4, 2015

Text removed from book below


If you want to arrange multiple plots on a single page, you'll need to learn a little bit of grid, the underlying graphics system used by ggplot. The key concept you'll need to learn about is a viewport: a rectangular subregion of the display. The default viewport takes up the entire plotting region, and by customising the viewport you can arrange a set of plots in just about any way you can imagine. \index{Layout} \index{Publication!multiple plots on the same page}

To begin, let's create three plots that we can experiment with. When arranging multiple plots on a page, it will usually be easiest to create them, assign them to variables and then plot them. This makes it easier to experiment with plot placement independent of content. The plots created by the code below are shown in Figure \ref{fig:layout}.

r columns(3)

a <- ggplot(economics, aes(date, unemploy)) + geom_line()
b <- ggplot(economics, aes(uempmed, unemploy)) + geom_point() + geom_smooth(se = FALSE) 
c <- ggplot(economics, aes(uempmed, unemploy)) + geom_point()
a
b
c

Subplots

One common layout is to have a small subplot drawn on top of the main plot. To achieve this effect, we first plot the main plot, and then draw the subplot in a smaller viewport. Viewports are created with (surprise!) the viewport() function, with parameters x, y, width and height to control the size and position of the viewport. By default, the measurements are given in 'npc' units, which range from 0 to 1. The location (0, 0) is the bottom left, (1, 1) the top right and (0.5, 0.5) the centre of viewport. If these relative units don't work for your needs, you can also use absolute units, like unit(2, "cm") or unit(1, "inch"). \index{Sub-figures} \index{Subplots}

library(grid)
# A viewport that takes up the entire plot device
vp1 <- viewport(width = 1, height = 1, x = 0.5, y = 0.5)
vp1 <- viewport()

# A viewport that takes up half the width and half the height, 
# located in the middle of the plot.
vp2 <- viewport(width = 0.5, height = 0.5, x = 0.5, y = 0.5)
vp2 <- viewport(width = 0.5, height = 0.5)

# A viewport that is 2cm x 3cm located in the center
vp3 <- viewport(width = unit(2, "cm"), height = unit(3, "cm"))

By default, the x and y parameters control the location of the centre of the viewport. When positioning the plot in other locations, you may need to use the just parameter to control which corner of the plot you are positioning. The following code gives some examples.

# A viewport in the top right
vp4 <- viewport(x = 1, y = 1, just = c("right", "top"))
# Bottom left
vp5 <- viewport(x = 0, y = 0, just = c("right", "bottom"))

To draw the plot in our new viewport, we use the vp argument of the print() method. This method is normally called automatically whenever you evaluate something on the command line, but because we want to customise the viewport, we need to call it ourselves. The result of this is shown in Figure \ref{fig:subplot-1}.

pdf("figures/polishing-subplot-1.pdf", width = 4, height = 4)
subvp <- viewport(width = 0.4, height = 0.4, x = 0.75, y = 0.35)
b
print(c, vp = subvp)
dev.off()

This gives us what we want, but we need to make a few tweaks to the appearance: the text should be smaller, we want to remove the axis labels and shrink the plot margins. The result is shown in Figure \ref{fig:subplot-2}.

csmall <- c + 
  theme_gray(9) + 
  labs(x = NULL, y = NULL) + 
  theme(plot.margin = unit(c(1/4, 0, 0, 0), "lines"))

pdf("figures/polishing-subplot-2.pdf", width = 4, height = 4)
b
print(csmall, vp = subvp)
dev.off()

\begin{figure}[htbp]
\centering
\subfigure[Figure with subplot.]{
\includegraphics[width=0.5\textwidth]{figures/polishing-subplot-1}
\label{fig:subplot-1}
}%
\subfigure[Subplot tweaked for better display.]{
\includegraphics[width=0.5\textwidth]{figures/polishing-subplot-2}
\label{fig:subplot-2}
}
\caption{Two examples of a figure with subplot. It will usually be necessary to tweak the theme settings of the subplot for optimum display.}
\label{fig:subplot}
\end{figure}

Note we need to use pdf() (or png() etc.) to save the plots to disk because ggsave() only saves a single plot.

Rectangular grids

A more complicated scenario is when you want to arrange a number of plots in a rectangular grid. Of course you could create a series of viewports and use what you've learned above, but doing all the calculations by hand is cumbersome. A better approach is to use grid.layout(), which sets up a regular grid of viewports with arbitrary heights and widths. You still need to create each viewport, but instead of explicitly specifying the position and size, you can specify the row and column of the layout.

The following example shows how this work. We first create the layout, here a 2 by 2 grid, then assign it to a viewport and push that viewport on to the plotting device. Now we are ready to draw each plot into its own position on the grid. We create a small function to save some typing, and then draw each plot in the desired place on the grid. You can supply a vector of rows or columns to span a plot over multiple cells. The results are shown in Figure \ref{fig:layout-2}.

r columns(1)

grid.newpage()
pushViewport(viewport(layout = grid.layout(2, 2)))

vplayout <- function(x, y) 
  viewport(layout.pos.row = x, layout.pos.col = y)
print(a, vp = vplayout(1, 1:2))
print(b, vp = vplayout(2, 1))
print(c, vp = vplayout(2, 2))

By default grid.layout() makes each cell the same size, but you can use the widths and heights arguments to make them different sizes. See the documentation for grid.layout() for more examples.

@baptiste
Copy link
Contributor

baptiste commented Aug 5, 2015

FWIW the latest version of grid.arrange has gotten quite flexible, it might be worth mentioning for users who'd rather not learn about viewports but just want the equivalent of par(mfrow) (and something compatible with ggsave). There's also annotation_custom in ggplot2 for inset plots.

@hadley
Copy link
Member Author

hadley commented Aug 5, 2015

Since you probably know more about this than me, would you be interested in drafting the vignette?

@baptiste
Copy link
Contributor

baptiste commented Aug 5, 2015

OK, I'll give it a go, but quite busy at the moment with teaching etc.

@baptiste
Copy link
Contributor

baptiste commented Aug 8, 2015

started, output rendered on this wiki (but losing fig captions).

@hadley
Copy link
Member Author

hadley commented Aug 9, 2015

Looks good so far! One thing to bear in mind is that since the vignette is included in the package, it needs to be kept fairly small and not use too many graphics. If you want to use lots of pictures, we'll probably have to publish a different way.

@baptiste
Copy link
Contributor

baptiste commented Aug 9, 2015

On 9 August 2015 at 15:39, Hadley Wickham notifications@github.com wrote:

Looks good so far! One thing to bear in mind is that since the vignette is
included in the package, it needs to be kept fairly small and not use too
many graphics. If you want to use lots of pictures, we'll probably have to
publish a different way.

it's likely to be (become) too long for a brief reference document, I
agree. Anyway, for now I'll keep adding content, and later you can pick the
pieces that seem most useful for a new user. Arguably,

grid.arrange(p1, p2, nrow=1)

is much more concise than a discussion of grid viewports, and will probably
satisfy most basic needs.

@0yliu
Copy link

0yliu commented Mar 16, 2016

Does grid.arrange working right now? I wrote a code in the beginning of this year (2016) for aligning two plots side by side (with unit.pmax adjustment for heights). it was working fine. But the exactly same code does not working right now. Is there any update that we should've know? Thanks a lot!

@hadley
Copy link
Member Author

hadley commented Jan 25, 2017

@baptiste any interest in finishing this off? Otherwise I'll close this issue as it's unlikely that I'll ever have time to do it.

@baptiste
Copy link
Contributor

here's the latest version for reference, but it's unlikely I find motivation to come back to it

https://github.com/baptiste/gridextra/wiki/arranging-ggplot

let me know if you want the Rmd source

@hadley
Copy link
Member Author

hadley commented Jan 25, 2017

ccing @wilkelab and @thomasp85 in case they have any interest in leading on this.

@thomasp85
Copy link
Member

I think gtable should probably have some vignette of some sort (maybe with a short section on its tie to grid layouts)

I'm over my head in projects right now though so it's not something that will come from me anytime soon...

@baptiste
Copy link
Contributor

here's what I started writing re gtable a few years ago:
https://github.com/baptiste/gridextra/wiki/gtable

@thomasp85
Copy link
Member

closing this... I think there are better venues for this than in a ggplot2 vignette

@EchoDelta7272
Copy link

Hello and thank you for sharing this vignette.

If I wanted to add a common y-axis title for multiple graphs displayed in a single column after employing the following:

gA <- ggplotGrob(Temp250m)
gB <- ggplotGrob(Temp2008m)
gC <- ggplotGrob(Temp3828m)
gD <- ggplotGrob(Temp4325m)

grid::grid.newpage()
grid::grid.draw(rbind(gA, gB, gC, gD))

Can I ask how to go about doing that?

My apologies if this is not the place to ask questions.

Thanks for any guidance

@teunbrand
Copy link
Collaborator

teunbrand commented Jul 22, 2023

@EchoDelta7272 for usage questions it is generally easier to ask them on the Posit community or stackoverflow, both because it keeps issues focussed and because there is a lot more people available to consider your question.

@EchoDelta7272
Copy link

Understood and thank you for the guidance @teunbrand!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants