使用knitr如何添加子图或子标题?

28

使用knitr可以产生带有子标题的子图吗?以下是一个最小化的工作示例:

\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}

这将产生两个标为“Figure 1”和“Figure 2”的图,其标题已定义(分别)。但是我希望它们被标记为“Figure 1a”和“Figure 1b”,就像使用subcaption LaTeX包一样。

我知道有一个knitr选项“fig.env”,但这并不能解决它(至少不使用“fig.env ='subfigure'”等)。在此有一个类似的帖子关于Sweave,但解决方案是一个不太优雅的hack:http://texblog.org/2011/12/01/sweave-subfig-controlling-figure-size-and-placement/


6
在 knitr 中完全可以做到这一点,因为您可以通过 knit_hooks$set(plot = function(x, options) {...}) 重新定义“plot”钩子,并以任何您想要的方式排列图形。唯一的问题是可能需要一段时间才能理解内部结构:https://github.com/yihui/knitr/blob/master/R/hooks-latex.R 对于我来说,支持子图似乎是一个不错的主意,您可以在这里提出功能请求:https://github.com/yihui/knitr/issues 谢谢! - Yihui Xie
1
太棒了!在 Github 上有人提出了一个功能请求,我看到你已经接受了挑战。 - dynamo
具体功能请求:https://github.com/yihui/knitr/issues/388 - Brian Diggs
3个回答

38

knitr(≥ v1.5)支持子图。您可以使用代码块选项fig.subcap。这里是一个最小的例子。

\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}

knitr中的子图


2
太棒了!这肯定会使我的源代码更加清洁。谢谢Yihui。 - JAponte
4
谢谢你制作这个工具!让很多事情变得更容易了。不过,我真的很难想象一个可以在绘图之间插入换行符的钩子(“智能地”或是由用户指定)。比如:如何使plot.lm的结果以2x2的布局显示,其中每个图是一个子图?(即不使用par(mfrow=c(2,2)),而是在第2个和第3个图之间使用\\\)。我应该在GitHub上提出特性请求吗?还是有简单的解决方法?谢谢! - dynamo
@Yihui 有没有办法让这些图像也居中在页面上? - radek
\usepackage{subfig} 这部分很重要 =);同时我可以确认这个功能现在已经在主线 knitr 中了。 - russellpierce
@Yihui,这太棒了。我不知道使用subcaption代替它会有多难。这可能还允许我们使用子表格。 - Sam
@dynamo 好久不见(将近四年),但终于还是实现了:https://github.com/yihui/knitr/issues/1327#issuecomment-346242532 - Yihui Xie

15

根据Yihui的答案并反映Rmarkdown在过去几年中的变化,以及远离Rnw文件。

如在knitr 块选项中列出的那样,子图需要在块头中设置一些附加设置:

  • fig.subcap 是子图标题的列表
  • fig.ncol: 子图的列数
  • out.width: 图片的输出宽度。通常将此设置为 100% 除以子列数。

子图还需要LaTeX包subfig。因此,在YAML中必须包含行\usepackage{subfig},或者如果您正在使用外部tex模板,则可以将其添加到该文件中。

以下是基本模板:

---
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)
```

这里输入图片描述

与ggplot2一起使用

如果你正在绘制包含多个ggplot图的子图,它们可能会很混乱,因为它们在各个图中不对齐。你可能想查看这篇文章,了解如何使用cowplot强制ggplots具有相同的尺寸。

这篇stackoverflow帖子将帮助你确保所有图像都像下面的图像一样对齐:

这里输入图片描述

提供子图列表

我只是想分享一个子图的可能扩展性。我喜欢用它们来处理图形列表。也许你已经编写了一个需要在四个不同试验组或四个不同位置上运行的函数。例如,这里是英国的四个城市的列表:

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

可以使用fig.sublapply生成子图列表。需要运行的话,就可以使用此列表。

```{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))
  )
```

输入图像描述

这使得子图非常健壮。如果我需要在额外的几个城市运行我的模型,我只需要将额外的值添加到location列表中,子图的长度将保持不变。


@Michael Harper 的解决方案很好。但是,当我有更多的图形超过一页时(对于 PDF 文档),我遇到了一个问题。例如,当我有 10 张图片时,在将文档编织成 PDF 后,剩余的图片会被简单地截断。你知道如何解决这个问题吗?我在这里创建了一个问题 here - Kevin

6
你可以使用Latex的subcaption包。
\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


2
不错的解决方案 - 它避免了显式制作图形PDF然后插入的步骤。但还是有点欠缺。期待在knitr中正式实现这个功能! - dynamo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接