在少数派,我已经接触过不少的 Markdown 编辑器。Markdown 的核心是将文字与排版分开,只用少量的符号标记几种文本元素,从而将注意力集中于文字本身。但究其本质,Markdown 还是 HTML 的简化版,最终排版依赖的格式来自于 css,最适合的输出格式还是 HTML 页面。当进行 PDF 和 Word 等等的文档排版时,由于文档独特的分页结构,必须解决图片和表格等浮动体的问题,尤其是图片不能割成两页。而网页却是连续的,根本无需考虑这个问题。类似这些差异总会让 Markdown 编辑器显得力不从心。

与右侧的HTML 文件相比,左侧的PDF 中图片出现了漂移
与右侧的HTML 文件相比,左侧的PDF 中图片出现了漂移

在文档排版上,另一个选择是 LaTeX,相较于 Markdown,LaTeX 是专门为 PDF 排版而生的,但其复杂程度就要比 Markdown 高得多。有没有什么办法把Markdown 的书写便利性和 LaTeX 丰富的排版能力结合到一起呢?另一方面,在写作中遇到数据处理、图表绘制时,往往需要先将图表独立生成,然后再行插入文档当中。这样,一来可能破坏已经成型的文档结构,二来依然会分散自己的写作精力。机缘巧合,在学习R 语言的时候,我接触到了这样一种另类的 Markdown 写作方式——Rmarkdown,可以用来解决这两个问题。这篇文章以使用 Rmarkdown 输出 PDF 文档为例,来说一说它的主要用法。

R 是什么?

Markdown 前面的R 又是什么呢?简单来说,R是一种主要面向数据处理的开源编程语言。相比其他昂贵的数据处理软件,R 最大的优势在于其开源而带来的免费性和强大的扩展能力,同时其语言上手难度也不算太高。由此,R 获得了很多科研工作者的青睐。不过 R 语言本身并非本文重点,即使你对它不甚了解,也并不耽误你使用 Rmarkdown。当然,如果你会使用它,你就更能对 Rmarkdown 得心应手。

RStudio + LaTeX + Pandoc

完整使用 Rmarkdown 需要 Rstudio、LaTeX 和 pandoc 三者的组合。首先来依次介绍一下它们。

RStudio

RStudio 是专门针对 R 开发的一款 IDE。而 Rmarkdown 正是 RStudio 网站开发的一个项目,旨在通过 Markdown 结合 R 来完成文章写作。你可以在安装了 R 的 GUI 主程序之后,在 Rsudio 的网站下载自己操作系统相应的安装包进行安装。软件打开后,可以看到三部分窗口,左边是 Console 窗口,用来运行 R 命令。如果你是 Mac 用户,RStudio 还会在 Console 窗口右侧默认打开一个 Terminal 窗口,更方便地运行终端里的命令。右上方窗口用以显示当前环境中的数据,或者可以切换显示运行过的代码历史。右下方的窗口是文件、已安装的包、绘制图形和帮助文档等的显示区域。新建一个脚本之后,会在左上方出现一个新窗口,用来编写 R 语言脚本,这也是用来编写 Rarkdown 文档的部分。

RStudio
RStudio

做为 RStudio 的扩展,使用 Rmarkdown 需要安装其他扩展包。可以在菜单栏「File」下选择新建一个文件,然后在文件格式中选择 R Markdown。这时软件会弹窗提示需要安装的包。点击确认等待安装完成即可。除了这里需要安装的包,针对中文支持,还需要再安装 rticles 包。具体方式可以在 Console 窗口运行如下命令:

install.packages("rticles")

安装完它们之后,你已经可以新建一个 Rmarkdown 文档并开始编辑生成一个 HTML 文档了。不过要想输出一个格式规整的 PDF 文档,还需要进一步安装 LaTeX 和 pandoc。

LaTeX 和pandoc

LaTeX 是一种通过宏语言进行文档排版的排版系统。对于 Windows 用户来说,RStudio 推荐安装体积较小的 Miktex,Mac 用户则被推荐安装 MacTex(其实 MikTeX 也有 Mac 版,但为什么没推荐我也不清楚)。LaTeX 还有其他诸如 Tex Live 等版本,本质上是相同的,这里就不对其进行赘述了。大部分的 LaTeX 安装包体积都比较大,因为其中包含了各式各样的包和文档等等。新手一般都会被推荐安装比较完整的 LaTeX 安装包(而非 basic 包)。不过如果你只是单纯地想使用 Rmarkdown,选择体积较小的 MikTeX 就足够了。在输出 PDF 文档时 MikTeX 会提示需要安装一些更多的包,到时再确认安装即可。

另一个需要安装的是 pandoc,它是一把用于文档转换的瑞士军刀。你几乎可以通过它进行任意格式的文档转换,同时排版结果也不失美观。其实 Rmarkdown 的功能核心就在于将 MarkDown 文档通过 pandoc 转换为 LaTeX 文档,然后利用 LaTeX 排版输出为 PDF,也是通过这种方法,我们在输入时可以利用 Markdown 的便利性,在排版时利用 LaTeX 的丰富性。在少数派的这篇如何把思维导图秒变成幻灯? 里,也曾经提到过 pandoc 的另一个有趣的用法。你可以在pandoc 的网站进行下载安装,如果你的电脑装有 Homebrew 的话,也可以直接运行以下命令:

brew install pandoc

安装完所有需要的工具,接下来就可以开始正式的文档写作了。

更丰富的文档排版能力

到此为止,已经可以在菜单栏中选择新建一个 R Markdown 格式文档了。点击后会弹出一个对话框用以选择文档格式。这里我们在左侧栏中选择 From Template,然后在右侧选择 CTeX Documents,点击确定后即会生成一个 .Rmd 文档,文档中会有开发者关于中文 Rmarkdown 的一些介绍。

从模版新建一个R Markdown 文档
从模版新建一个R Markdown 文档

除了熟悉的 Markdown 语法,你会发现在文档的最开始多了一部分用--- 括起来的段落。这一部分是用来设置文档输出格式的属性的,语法类型为 YAML。实际上这部分的用法也比较简单,只需要在参数后加一个冒号,再写上值即可。一些参数可能存在着层级关系,使用缩进表示。最常用的参数包括 title 即文档标题、author 即作者、date 即日期,具体形式如下:

title: "title"
author: 
 - author1
 - author2
date: "2018/01/14"

涉及中文文档时,最主要的参数设置为:

---
documentclass: ctexart
output: 
    rticles::ctex
---

在使用习惯以后,你可以不必依赖模版,只要在新建文档中规定好它们,就可以顺利地编译出中文文档。这里,documentclass 规定了文档的类型,ctexart 是 LaTeX 中兼容了中文的文档类。而 output 是对输出格式的具体设置,这里的 rticles::ctexrticles 包中针对中文优化过后的PDF 输出格式。你可以在下一层级的命令里继续设置 PDF 文档的其他详细参数,新建的模版中已经有三个参数。对于常用的 PDF 文件,Rmarkdown 提供了足够的自定义参数满足需求,包括目录格式、图片格式、表格样式、语法高亮形式、纸张大小、页边距、字体字号甚至链接颜色等等。如果你觉得这些还不够,你也可以直接在 includes 参数下加入 LaTeX 命令或文件作为文档的 header,或插在 body之前或之后。如果能力所及,你还可以直接换一个自己的模版……通过详细的参数和丰富的可定制性,文档排版及格式可以变得更加灵活多样。具体的参数,可以参考官方网页

以上所述的所有部分,都只是在 YAML 部分完成的,而没有涉及文档主体的 Markdown 部分。这样,当进入 Markdown 写作部分时,我们又可以专心于文字,不必担心将格式和内容搅在一起了。在这里,你可以使用通用的 Markdown 语法,当然也支持插入 MathJax 公式,一切和其他 Markdown 编辑器一样。如果对语法有疑问,在菜单栏的 Help 下点击“Markdown Quick Reference”就能浏览详细语法了。

不过,Markdown 偶尔还是会有一些无法在 PDF 页面上达成的排版精细活儿,这时候,Rmarkdown 还支持直接在文档里插入 LaTeX 命令,使用它完成即时所需的特殊排版需求。可以说在 Markdown 和 LaTeX 二者的融合上,Rmarkdown 给出了一个非常自由而又灵活的解决方案。

在 Rmarkdown 里直接插入 LaTeX 命令
在 Rmarkdown 里直接插入 LaTeX 命令

更直接的代码运行能力

除了比传统的 Markdown 编辑器更加适合 PDF 排版,Rmarkdown 的另一大杀手锏就是它可以在生成文档时直接运行插入的代码块,并将运行结果打印在最终文档中。而且,它不仅支持 R 语言一种方式,你还可以使用 Python、Java Script 等等其他语言生成你想要的结果。在以数据处理为核心的论文写作中,这是一种非常实用而又能大大提升写作效率的方式。其次,文档中的代码可以边写边运行调试,每个代码块的运行结果会直接显示在该代码块的下方。除去文字部分,文档和一个普通的脚本并无差别,而在文章中也能更方便地引用数据结果。

可运行代码块的书写方式与在 Markdown 中插入普通代码块类似,插入块状代码时,在代码块语法开始的``` 之后写上一个大括号,并在括号中写入语言的名称即可。例如,如果需要插入一段 r 代码,可用如下形式:

```{r}
1 + 1
```

之后,你就可以在输出文档中看到该代码本身,和作为输出结果的“2”。以此类推,在需要绘制图表时,直接插入代码,就可以在文章中显示图表了。

使用代码绘图并打印
使用代码绘图并打印

插入可运行的行内代码,可以直接在第一个` 后输入一个小写的 r ,然后插入代码本身。这在行文中需要引用计算结果时非常便利,例如文档里写入:

1 + 1 = `r 1 + 1`

你会在输出文档中直接得到“1 + 1 = 2”。

当然,并不是所有时候都需要把代码放进最后的成文里。Rmarkdown 提供了各种设置代码块属性的命令,用来设置代码块的名称、显示和运行方式、以及结果的输出方式等等。你可以在大括号中的r 后直接输入代码块的标签(这在代码块过多时可以帮助你快速定位),然后打一个逗号, 继续输入其他参数,参数与值之间用等号连接,而参数与参数之间仍然用逗号, 分隔。比较常用参数的包括 echo 设置是否在文档中包含代码块,eval 设置代码是否运行,results 设置运行结果的输出形式,messagewarning 设置是否打印 message 和 warning 信息等等。你会看到每个代码块的右上角有一个用来设置常用属性的图形按钮,你也可以在这里设置属性。以下面的代码为例:

```{r example label, echo = FALSE, warning = FALSE}
# coding
```

这里的代码即为设置了代码块的名称为 example label,然后使代码块不包括在文档中,同时不输出警告信息。我们可以将上面图片中绘制图形的代码部分隐藏,效果如下:

隐藏生成上述图片的代码
隐藏生成上述图片的代码

除此之外,还有针对图片输出格式等等的更多细节属性设置,详细的参数可以参见帮助文档。如果你想设置全局的代码块属性,可以在文档开头加入如下代码:

```{r setup}
knitr::opts_chunk$set()
```

显而易见,该命令就是设置代码块选项的。在括号中填写需要更改的参数即可,语法与在每个代码块中设置属性时相同。到此为止,你已经可以顺利地在文档中插入可运行的代码,并且设置每个代码块的属性了。

使用其他语言

如果你对 R 语言并不熟悉,你也可以通过设置改用其他语言的代码。使用方法也很简单,这里以 Python 为例,只需要在插入代码时将大括号里的 r 写为 python 即可:

```{python}
python code...
```

不过,如果你想使用不同于默认调用的 Python 版本,可以在```{Python,后加上命令engine.path = " ",引号中填入想要的 Python 地址即可。如果设置全局的 python 引擎,同样可以依照以上方法在 kintr::opts_chunk$set() 中更改 engine.path 属性即可。关于 Rmarkdown 支持的语言和更多相关细节,可以参见[文档页]。(http://rmarkdown.rstudio.com/authoring_knitr_engines.html)。

输出文档

文档完成后,就可以输出 PDF 文件了。文档生成只需点击脚本窗口上方的 knit 按钮即可。

点击knit 输出文档
点击knit 输出文档

唯一需要注意的就是 .Rmd 文档的文件名不能包括中文,否则 pandoc 会报错。点击按钮后程序会自动运行,一切顺利的话就会在 .Rmd 文档同一路径下输出一个 PDF 了。另外,如果之前安装的是 MikTeX 的话,输出文档时会提示你安装所需的包,点击安装即可。

其他文档类型

虽然 LaTeX 拥有丰富的排版能力,不过它还是有一定难度。相比之下,使用 MS word 的人可能更多,在需要与他人合作时,也可能不得不使用 Word。仰赖于 pandoc 强大的转换能力,Rmarkdown 自然也可以输出 Word 文档。在新建 .Rmd 文档时,就可以在弹窗中选择新建一个 .docx 格式文档。实际上,在 YAML 部分的 output: 参数下写上 word_document就可以完成相同的工作。同样,也可以在下一层级的属性中规定 word 文档的输出格式。更多的详细属性可以参见官方介绍页

不过相比 LaTeX 输出,Word 文档的可定制性就差很多。但 Rmarkdown 提供了一个有趣的自定义样式的方式。你可以在 word_document: 下规定一个 reference_docx: 的属性,在后面填入作为模版的文档。而模版文档需要在 Word 的“样式”中分别设置每部分文体(即正文、一二三级标题等)的详细格式。根据 Rmarkdown 官方的说法,为了保证格式模版好用,最好使用 Rmarkdown 导出的文档作为模版。在实际操作中,Word 总会不出意外地给出一些出人意料的惊喜,所以可以输出一次文档,更改一次格式作为模版,然后再使用该模版输出新文档,修改后作为下一次的模版……反复几次微调之后就可以得到自己想要的模版了。更详细的介绍还可以参见这个教程

除了 Word 文档之外, HTML 文档的输出能力自然也毋庸置疑。Rmarkdown 支持自定义的 css 用于更改输出样式,同时提供了其他的可定制性。如果对执行代码有需求,也可以尝试用它生成静态 HTML 文件。更多说明可以参见官方文档页

虽然 Rmarkdown 比传统的 Markdown 编辑器拥有了更丰富的排版选择,但它还是会有自己的局限之处。你可能会问,这么麻烦我直接用 LaTeX 不就好了?如果更习惯 LaTeX 又想使用 RStudio 运行代码的能力,你还可以选择新建一个 .Rnw 文档,它其实是 Rmarkdown 的 LaTeX 形式,然后直接用 LaTeX 写文档就可以了。

尾巴

这篇文章面对 Rmarkdown 新手,所以有些叙述比较简单,略显琐碎。其实我自己也只是在学习过程中,只能到应用水平,对原理了解不多,发现问题的各位欢迎在评论里进行交流。除了上文提及的优点, Rmarkdown 的另一个优点是写好一个文档后,更换一下源头的数据就可以生成一篇完全一样的文章。进行重复研究时,使用行内代码的方式就无需再手动从每个角落里更改计算结果。

Markdown 的写作方式的确方便,但它还是专门针对网页而生的。现实使用中,我们有时又不得不面对文档生成的情况。如 Rmarkdown 的主要开发者谢益辉在这篇 Markdown or LaTeX? 中所言,网页是连续的,而文档需要断页。这就带来了很多浮动体的问题,图片不可能拆开放在两页,网页也没有页眉页脚页码这种麻烦。随着电子设备的普及、文档电子化的进展,可断页文档真的还必要吗?如非需要打印,何必要用传统文档格式保存文件呢?大多时候,这件事只是一种削足适履的资源浪费。再说,很多时候,又何苦非要把文档打印出来呢?

除此之外,RStudio 毕竟不是针对文档写作开发的软件,Rmarkdown 的核心还是运行代码。所以如果没有运行代码的需求,常用 Markdown 编辑器的写作体验自然要好更多。其实我常用的写作工具 Mweb 就支持编辑 .Rmd 文档。工具的选择最终还是要依赖工作本身,无需本末倒置。多一个选择,是为了在需要时找到一条提高效率的捷径。