28

Is it possible to produce subfigures (with associated subcaptions) using knitr? Here is a minimal working example:

\documentclass{article}

\begin{document}

<<echo = FALSE, fig.cap = c("Some numbers.", "Some more numbers."), out.width = "0.5\\textwidth", fig.align = "center">>=

plot(1:10)
plot(30:100)

@

\end{document}

This results in two figures labelled Figure 1 and Figure 2 with captions as defined (respectively). But I want them to be labelled "Figure 1a" and "Figure 1b", as you can do with the subcaption LaTeX package.

I know there is a knitr options "fig.env", but this doesn't solve it (at least not using, for example, "fig.env = 'subfigure'"). There is a similar post here regarding Sweave, but the solution is an inelegant hack: http://texblog.org/2011/12/01/sweave-subfig-controlling-figure-size-and-placement/

dynamo
  • 2,988
  • 5
  • 27
  • 35
  • 6
    It is entirely possible in knitr because you can redefine the `plot` hook by `knit_hooks$set(plot = function(x, options) {...})` and arrange the plots in whatever way you want. The only problem is it may take a while for you to understand the internals: https://github.com/yihui/knitr/blob/master/R/hooks-latex.R It sounds like a good idea for me to support subfigures internally, and you can put a feature request here: https://github.com/yihui/knitr/issues Thanks! – Yihui Xie Sep 22 '12 at 21:08
  • 1
    Cool! Feature has been requested on github - and I see you've already taken up the gauntlet. – dynamo Oct 01 '12 at 08:00
  • Specific feature request: https://github.com/yihui/knitr/issues/388 – Brian Diggs Oct 18 '12 at 16:02

3 Answers3

38

knitr (>= v1.5) supports subfigures. You can use the chunk option fig.subcap. Here is a minimal example.

\documentclass{article}
\usepackage{subfig}
\begin{document}

<<fig-sub, fig.cap='two plots', fig.subcap=c('one plot', 'the other one'), out.width='.49\\linewidth'>>=
plot(1:10)
plot(rnorm(10), pch=19)
@

\end{document}

subfigures in knitr

Yihui Xie
  • 28,913
  • 23
  • 193
  • 419
  • 2
    Great! This will definitely keep my source code cleaner. Thanks Yihui. – JAponte Dec 05 '12 at 19:38
  • 4
    Thanks for making this! Makes many things much easier. However, I am really struggling to imagine a hook that will allow linebreaks between plots (either "intelligently" or by user specification). Such as: How to get the result of `plot.lm` to show in a 2x2 configuration where each plot is a subfigure? (I.e. not using `par(mfrow=c(2,2))`, but with a `\\\` between plot 2 and 3. Should I throw a feature request on GitHub? Or is there a simple solution? Thanks ! – dynamo Feb 07 '13 at 09:17
  • @Yihui Is there a way to also have these figures centred on a page? – radek Jan 17 '14 at 14:25
  • The \usepackage{subfig} part is important =) ; also I can confirm that this functionality is now in mainline knitr. – russellpierce Apr 11 '14 at 20:09
  • @Yihui, this is awesome. I do not know how difficult it will be to use `subcaption` instead of this one. It will probably allow us to use sub table as well. – Sam Mar 03 '15 at 18:11
  • @dynamo It's been a while (nearly four years), but finally implemented anyway: https://github.com/yihui/knitr/issues/1327#issuecomment-346242532 – Yihui Xie Nov 22 '17 at 05:31
14

Updating the answer from Yihui to reflect the changes in Rmarkdown in the past few years, and also the shift away from Rnw files.

As listed within the knitr chunk options, subfigures require a few additional settings to be set in the chunk header:

  • fig.subcap is a list of the captions for subfigures
  • fig.ncol: the number of columns of subfigures
  • out.width: the output width of the figures. You will normally set this 100% divided by the number of sub columns.

Subfigures also require the LaTeX package subfig. The line \usepackage{subfig} must therefore be included within the YAML, or if you are using an external tex template you can add this to that file.

Here is a basic template:

---
output: pdf_document
header-includes:
   - \usepackage{subfig}
---  

```{r fig-sub, fig.cap='two plots', fig.subcap=c('one plot', 'the other one'), out.width='.49\\linewidth', fig.asp=1, fig.ncol = 2}
plot(1:10)
plot(rnorm(10), pch=19)
```

enter image description here

Using with ggplot2

If you are plotting subfigures which contains multiple ggplot plots, can messy as they do not line up between the plots. You may want to check out this post here on how to use cowplot to force ggplots to have the same dimensions.

This stackoverflow post will help you make sure that all the plots line up as shown in the image below:

enter image description here

Providing a list to subfigures

Just sharing this as a possible extension of subfigures. Something I like to use them for is feeding them a list of figures. Maybe you have produced a function which you need to run on four different trial groups, or in four separate locations. For example, here is a list of four cities in the UK:

```{r}
locations <- c("Southampton, UK", "London, UK", "Bristol, UK", "Birmingham, 
```

This list can then be used within the fig.sub and an lapply to produce a list of subfigures. If you need to run the

```{r fig-sub-2, fig.cap='A collection of maps', fig.subcap= locations, out.width='.49\\linewidth', fig.asp=1, fig.ncol = 2}
library(ggmap)
lapply(locations, function(x) 
  ggmap(get_map(x))
  )
```

enter image description here

This makes the subfigures quite robust. If I need to run my model in an extra couple of cities, all I need to do is add extra values to the location list and the sub figures will be the same length.

Michael Harper
  • 14,721
  • 2
  • 60
  • 84
  • Nice solution @Michael Harper. However I have a problem with this when I have more figures then fit on one page (for pdf document). When I have lets say 10 figures the remaining figures simply get cut-off after knitting the document to a pdf. Do you know how to solve this? I created a question for it as well [here](https://stackoverflow.com/questions/61526548/subfigures-or-subcaptions-with-knitr-that-span-more-than-one-page). – Kevin May 14 '20 at 14:00
6

You can use Latex's subcaption package.

\documentclass{article}
\usepackage{subcaption}

\begin{document}

\begin{figure}
\centering
\begin{subfigure}[b]{0.3\textwidth}
\centering
<<echo = FALSE, out.width = "0.5\\textwidth", fig.align = "center">>=
  plot(1:10)
@
\caption{text for first figure}
\label{fig:gull}
\end{subfigure}%

\begin{subfigure}[b]{0.3\textwidth}
\centering
<<echo = FALSE, out.width = "0.5\\textwidth", fig.align = "center">>=
  plot(30:100)
@
\caption{text for second figure}
\label{fig:tiger}
\end{subfigure}
\caption{Figure caption}
\end{figure}

\end{document}

enter image description here

JAponte
  • 1,508
  • 1
  • 13
  • 21
  • 2
    Nice solution - it saves explicitly making the graphic pdf and then inserting it. But it's still a bit of a hack. Looking forward to a proper implementation of this in knitr! – dynamo Oct 19 '12 at 06:40