原文链接 🔗 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) 。请注意,这里主要使用的 knitrpandoc 不需要进行自定义设置,保持默认设置就可以。我希望你做的只是使用这些工具的相关选项和设置,以及一些模板和文档示例,它们展示了如何在实际操作中生成漂亮的输出结果。

我在 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 文件如下方代码块所示,它使用一些变量作为简写,以及像 $@$< 这样的特殊变量,分别表示「当前目标的名称」和「当前先决条件的名称」。

## 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 $@ $<
## 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 $@ $<
%.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 $@ $<
%.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 $@ $<
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-runmake docx --dry-runmake 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 和很多其他工具。 ↩︎