原文链接 🔗 5 Pull it Together(opens new window)

📚 合集|社会科学的纯文本指南(opens new window)


写论文时需要引用各种书籍和文章,一般情况下它们包括在 R(opens new window) 创建的表格和图片中。我们要做的是快速将包括这些图表内容的 Markdown 文件转换为格式正确的学术论文,而不损失任何必不可少的学术结构(在输出端)和 Markdown 的便利性和可转换性(在输入端)。我们希望从同一个来源便捷地获得美观的输出,例如 HTML、PDF 和 DOCX 格式,同时在成本最小化转换步骤之外(理想情况下),对输出后的结果不再进行其他处理,以上这些是我们能够做到的。

workflow-wide

纯文本工具链

示例文档流程如上图所示,看起来有点混乱,但我保证实际操作不像上面那么复杂,一股脑把它说清楚可能听起来有点疯狂。但是,实际上只有两个主要步骤。首先,knitr 将 .Rmd 文件转换为 .md 文件,其次,John MacFarlane(opens new window) 出色的 Pandoc(opens new window) 将 .md 文件转换为 HTML、、PDF 或 Word 格式。在这两个步骤中,我们使用一些转换模板和配置文件来达到工作又少结果又好的效果。为了使用某些工具,安装一些标准 Unix 开发者工具必不可少,这些工具可以 直接从命令行(opens new window) 安装在 macOS 上[1], 比如 R,knitr,Pandoc 和 发行版(opens new window) 。请注意,这里主要使用的 knitr 和 pandoc 不需要进行自定义设置,保持默认设置就可以。我希望你做的只是使用这些工具的相关选项和设置,以及一些模板和文档示例,它们展示了如何在实际操作中生成漂亮的输出结果。

我在 Emacs 中写所有的东西,在 前面(opens new window) 我们已经谈到了 Emacs,但用不用它取决于你,使用你喜欢的任何文本编辑器都可以,只是你觉得顺手。自定义的 样式文件(opens new window) 最开始被放在一起是为了让我直接写出漂亮的 .tex 文件,但现在它们只是在后台工作。Pandoc 使用这些 样式文件转换文件为 PDF,其中繁杂的工作由 org-preamble-pdflatex.sty(opens new window) 和 memoir-article-styles(opens new window) 文件来完成。

如果你把这些文件安装在 找得到的地方,也就是说,如果你可以编译这个 示例文档(opens new window) ,那就可以开始了。此外,你也可以使用我的 BibTeX 主文件(opens new window) ,但你可能想要使用自己的主文件,可以在模板中适当地修改引用内容。其次,我也制作了自定义的 Pandoc 模板,这是模板的 仓库(opens new window) ,实际上,很多文件都放在 ~/.pandoc/ 目录下,这是 Pandoc 的配置文件存放目录。此外,我还建立了一个示例 md-starter 项目(opens new window) 和一个 rmd-starter 项目(opens new window) ,这两个是用 Markdown(只有一个 .md 文件,没有 R)写的论文的项目文件夹骨架,以及用 .rmd 文件写论文的一个开篇。总的来说,这些模板包括基本的启动文件和 Makefile —— 用来生成 .html、.tex、.pdf 和 .docx 文件。

---
title: "A Pandoc Markdown Article Starter"
author:
- name: Kieran Healy
  affiliation: Duke University
  email: [email protected]
- name: Joe Bloggs
  affiliation: University of North Carolina, Chapel Hill
  email: [email protected]
date: January 2014
abstract: "Lorem ipsum dolor sit amet."
---
# Introduction
Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
sed do eiusmod tempor incididunt ut labore et dolore magna 
aliqua [@fourcade13classsituat]. Notice that citation.
# Theory
Lorem ipsum dolor sit amet, consectetur adipisicing 
elit, sed do eiusmod tempor incididunt ut labore et 
dolore magna aliqua [@fourcade13classsituat].
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
包含元数据的 Markdown 文件

让我们从一个简单的没有 R 代码的 Markdown 文件开始,因此就没有上图中 kniter 及其左边的部分。上方代码块展示了 article-markdown.md 这个实例文件的开头部分,上半部分是 Pandoc 可以读取的文档元数据,pandoc-templates 仓库(opens new window) 中的 HTML 和 模板被进行了适当设置,可以正确使用这些元数据。

学术工作中,参考文献的有效管理尤为重要。pandoc-citeproc(opens new window) 是 Pandoc 中处理参考文献的扩展,它可以和 pandoc 一起安装。参考文献可以多种格式存储(例如 BibTeX、EndNote),在 .md 文档中,参考文献的引入是通过引用键实现的,例如 [@healy14datavisualsociol]。当 pandoc 转换文档时,引用键被替换为参考文献,像(Healy & Moody,2014)这样,完整的参考文献条目会列在文后的参考文献中,更多关于引用的细节,可以阅读 Pandoc 说明文档(opens new window) 。此外,Pandoc 还可以处理文本标签和交叉引用,叫做 pandoc-crossref(opens new window) 的一个扩展实现了 @label 处理图片、表格、公式、定理、代码等的功能。

Pandoc 管理引用的方式不止一种,但在这里我们只使用最独立的方法。简单的文档可以用单个 .md 文件书写,包括数据分析的文档用 .Rmd 文件写,然后将 .Rmd 编译为 .md 文件,再转换成 PDF 或 HTML。这里给出一个最简单的例子, 文件 mypaper.md 可以通过打开终端窗口并输入下面的命令将其转换为 mypaper.pdf。

pandoc mypaper.md -o mypaper.pdf
1

# 用 make 实现自动化

因为我们可能会经常运行上面这些的命令,所以自动执行命令会更方便,并添加一些额外的附加功能来适应我们在文件中增减的内容,例如作者信息和其他元数据,以及处理参考文献和交叉引用的功能。这些都是由 pandoc 在命令行中通过各种操作完成的,并使用一些扩展来确保参考文献和交叉引用准确无虞。为了避免反复输入很长的命令,我们最好通过自动化来实现这一过程。当我们的最终输出文件在正确生成之前可能有许多先决条件时,这种自动化特别有用,并且我们希望电脑能更智能一点,知道什么需要重新处理,在什么条件下处理。这样的话,如果一张图片没有发生改变,我们将不会重新运行(可能很耗时的)R 脚本来再次创建它,除非我们必须这样做。

我们使用一个叫做 make(opens new window) 的工具来实现这个自动化流程。在我们的项目文件夹中,有一个纯文本文件 Makefile,其中包含一些规则,用于管理如何生成可能具有许多先决条件的目标文件。在这里,PDF 或 HTML 是目标文件,各种图片和数据表格是先决条件,如果生成图片和表格的代码发生更改,最终文档也将更改。make 从目标文档开始,沿着先决条件链向后工作,根据需要重新编译或重新创建它们。

make 是一个非常强大的工具!想要快速入门 make,可以看看 Karl Broman 的 minimal make(opens new window) (顺便提一下,Karl Broman 有 很多教程和指南(opens new window) ,可以为这里提到的许多工具提供准确而简洁的参考,例如 进行可复现的研究(opens new window) 、Git 和 GitHub 指南(opens new window) 、knitr 的简短介绍(opens new window) 。)

按照 Karl Broman 的例子,假设你有一篇用 Markdown 写的论文 paper.md ,它包含了一张由 R 脚本 fig1.r 生成的图片 fig1.pdf,当然可以有一个 .Rmd 产生输出的完整文件,但有些情况下这并不是最适合的。你的目标是得到 PDF 格式的论文,当 paper.md 中的文本发生变化时,paper.pdf 必须重新创建,同样,当我们更改 R 脚本 fig1.r,那么 fig1.pdf 也需要更新,因此 paper.pdf 还是需要重新创建,但是使用 make 我们可以简化处理这个过程。

下面是一个基本的 Makefile 示例:

## Read as "mypaper.pdf depends on mypaper.md and fig1.pdf"
mypaper.pdf: mypaper.md fig1.pdf
    pandoc mypaper.md -o mypaper.pdf
## Read as "fig1.pdf depends on fig1.r"
fig1.pdf: fig1.r
    R CMD BATCH fig1.r
1
2
3
4
5
6
7
一个简单的 Makefile

Makefiles 有个最大的问题,就是不知道为什么它使用 tab 键而不是空格来缩进与规则相关的命令。如果你使用空格,make 将无法正常工作。回到上述的 Makefile 示例,在命令行键入 make,它将会检查目标文件(makefile.pdf)及其所有依赖文件的状态。如果目标文件不存在,make 将根据指定规则创建,如果目标文件存在,make 将检查自上次创建以来其先决条件是否发生了更改。如果是,它将重新创建该文件,先决条件链向后传递,因此如果更改 fig1.r,在运行创建 mypaper.pdf 的规则中的命令之前,make 将注意到更改并重新运行来创建 fig1.pdf。当然,你可以选择只调用来自 makefile 的单个规则,例如,在命令行中输入 make fig1.pdf 而不是 make,这将只评估 fig1.pdf 的规则以及和它相关的先决条件。

对于上面这样一个简单的例子,make 主要是提供一个小小的便利,省去了一遍又一遍地输入一系列命令来创建论文的麻烦。但是,一旦你拥有包含许多文档和依赖项的项目,它就变得非常有用,例如,由不同章节组成的论文,每个章节都包含图片和表格,而这些图片和表格依赖于各种 R 脚本来进行设置和清理数据,这种情况下,make 就成为一种非常强大且有用的方法,可以确保最终的输出结果一定是最新的。

为了处理更复杂的项目和各种先决条件,make 可以使用许多变量来保存你键入 figure-x.pdf 目录中每个项的名称。

示例 md-starter 项目(opens new window) 中的 Makefile 可以按照指令将工作目录中任何的标记文件转换为 .html、.tex 或 .docx 文件。例如,在命令行输入 make html 会把目录中所有的 .md 文件转换为 .html 文件,输出的 PDF(由命令 make pdf 实现)看起来就像 这篇文章(opens new window) 。

Makefile 的不同部分定义了一些变量,用于指定不同文件类型之间的关系。实质上,根据规则,目录中的所有 PDF 文件都依赖于对 .md 具有相同名称的文件的更改,HTML 文件是如此, 文件也是这样。然后 pandoc 命令出现,将 Markdown 文件输出最终的结果。Makefile 文件如下方代码块所示,它使用一些变量作为简写,以及像 [email protected] 和 $< 这样的特殊变量,分别表示「当前目标的名称」和「当前先决条件的名称」。

## What extension (e.g. md, markdown, mdown) is being used
## for markdown files MEXT = md
## Expands to a list of all markdown files in the working directory
SRC = $(wildcard *.$(MEXT))
## Location of Pandoc support files.
PREFIX = /Users/kjhealy/.pandoc
## Location of your working bibliography file
BIB = /Users/kjhealy/Documents/bibs/socbib-pandoc.bib
## CSL stylesheet (located in the csl folder of the PREFIX directory).
CSL = apsa
## x.pdf depends on x.md, x.html depends on x.md, etc
PDFS=$(SRC:.md=.pdf)
HTML=$(SRC:.md=.html)
TEX=$(SRC:.md=.tex)
DOCX=$(SRC:.md=.docx)
## Rules -- make all, make pdf, make html. The `clean` rule is below.
all:    $(PDFS) $(HTML) $(TEX) $(DOCX)
pdf:    clean $(PDFS)
html:   clean $(HTML)
tex:    clean $(TEX)
docx:   clean $(DOCX)
## The commands associated with each rule. The first one is run with
## make html.
%.html: %.md
    pandoc -r markdown+simple_tables+table_captions+yaml_metadata_block \
    -w html -S --template=$(PREFIX)/templates/html.template \
    --css=$(PREFIX)/marked/kultiad-serif.css --filter pandoc-crossref \
    --filter pandoc-citeproc --csl=$(PREFIX)/csl/$(CSL).csl \
    --bibliography=$(BIB) -o [email protected] $<
## Same goes for the other file types.
## Watch out for the TAB before 'pandoc'
%.tex:  %.md
    pandoc -r markdown+simple_tables+table_captions+yaml_metadata_block \
    --listings -w latex -s -S --latex-engine=pdflatex \
    --template=$(PREFIX)/templates/latex.template \
    --filter pandoc-crossref --filter pandoc-citeproc \
    --csl=$(PREFIX)/csl/ajps.csl --filter pandoc-citeproc-preamble \
    --bibliography=$(BIB) -o [email protected] $<
%.pdf:  %.md
    pandoc -r markdown+simple_tables+table_captions+yaml_metadata_block \
    --listings -s -S --latex-engine=pdflatex \
    --template=$(PREFIX)/templates/latex.template \
    --filter pandoc-crossref --filter pandoc-citeproc \
    --csl=$(PREFIX)/csl/$(CSL).csl --filter pandoc-citeproc-preamble \
    --bibliography=$(BIB) -o [email protected] $<
%.docx: %.md
    pandoc -r markdown+simple_tables+table_captions+yaml_metadata_block \
    -s -S --filter pandoc-crossref --csl=$(PREFIX)/csl/$(CSL).csl \
    --bibliography=$(BIB) -o [email protected] $<
clean:
    rm -f *.html *.pdf *.tex *.aux *.log *.docx
.PHONY: clean
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
一个略显复杂的 Makefile

需要注意的是,pandoc 命令是单行文本解释,而不是由 return 键分隔的几行。但是可以使用 \ 符号告诉 make 继续到下一行而不中断。使用上述 Makefile 文件,键入 make pdf 将运行 Pandoc 命令,将文件目录中所有 .md 文件一次性全部转换为一个 PDF 文件,并使用 APSR(opens new window) 参考文献样式、我的 模板和一个名为 socbib-pandoc.bib 的 .bib 文件。

但是,不要盲目使用 Makefile,最好花点时间了解它的 make 工作原理以及它是如何帮助你的,make 官方手册(opens new window) 是非常清楚详细的。make 保守的先决条件链可能使编写复杂项目规则变得棘手,在编写或检查 Makefile 及其特定规则时,使用该 --dry-run 命令很有用 ,例如 make --dry-run,会输出 make 的命令列表,但不会执行。你可以在至少包含一个 .md 文件的目录下,尝试使用上述 Makefile 命令,例如,输入 make pdf --dry-run、make docx --dry-run 或 make clean --dry-run,看看会输出什么。

许多项目所需的特定步骤可能非常简单,并且不需要使用任何变量或其他不必要的东西。如果你发现自己反复运行相同的命令来生成文档(例如清理数据,初步运行代码,生成数据,输出最终文档),那么 make 可以做很多事情来自动化这一过程。有关 Makefiles 完成数据分析相关的更多示例,可以参考 Lincoln Mullen 对他使用 make 来完成工作的 论述(opens new window) 。

我的示例仓库 包含(opens new window) 一个.Rmd 示例文件,文件中的代码块提供了如何在文档中生成表格和图片的示例,特别是有一些可以传递给 knitr 的很有用的选项,可以参考 knitr 项目页面(opens new window) ,有大量的文档和更多的示例。

要从 article-knitr.Rmd 文件输出结果,你当然可以在工作目录中启动 R,加载 knitr 处理该文件,这将生成 article-knitr.md 文件,以及保存在 figures/ 文件夹中的一些图片(以及文件夹 cache/ 中的一些工作文件)。我们在 .Rmd 文件中进行设置,这样 knitr 就可以输出由 R 生成的图片的 PNG 和 PDF 版本,这为便捷地转换为 HTML 和 做好了准备。一旦 article-knitr.md 文件生成 .tex,就可以像以前一样在命令行输入 make 命令来生成 .html、.tex 和 .pdf 文件。但是,make 也可以自动完成第一步,rmd-starter(opens new window) 项目中有一个示例 Makefile,开头是 .Rmd 文件,结果将从那里输出。


  1. 可以这样操作:打开终端窗口,输入 xcode-select --install。也可以使用 Homebrew 包管理器(opens new window) 来安装 pandoc 和很多其他工具。 ↩︎