|
30 | 30 |
|
31 | 31 | ### R Markdown 与文学化编程 {#r-markdown-literate-programming} |
32 | 32 |
|
33 | | -初学者可能会认为 rmarkdown 软件包就是 R Markdown,但是事实并非如此。R Markdown 是文学化编程的一种实现方式,她包含了一系列的工具和软件包,而本书只能介绍其中有限的几个。文学化编程就是前面刚刚提及的**一个绝妙的想法**,她是高德纳(Knuth, Donald E.)在 1984 年提出的编程方法,旨在取代结构化编程范型。文学编程范型让程序员用他们自己思维内在的逻辑和流程所要求的顺序开发程序,而且用人类日常使用的语言写出来,就好像一篇文章一样。 |
| 33 | +初学者可能会认为 rmarkdown 软件包就是 R Markdown,但是事实并非如此。R Markdown 是文学化编程的一种实现方式,她包含了一系列的工具和软件包,而本书只能介绍其中有限的几个。文学化编程就是前面刚刚提及的**一个绝妙的想法**,那就是高德纳(Knuth, Donald E.)在 1984 年提出的编程方法,旨在取代结构化编程范型。文学编程范型让程序员用自己思维内在的逻辑和流程所要求的顺序开发程序,而且用人类日常使用的语言写出来,就好像一篇文章一样。 |
34 | 34 |
|
35 | | -文学化编程本来是为了编程和写文档两个目的而生的,想法看起来很好,但其实这两件事情都没有很方便地完成。编程通常就是代码加注释,大家都熟悉这个套路,没有人在代码之间长篇大论写文档来解释这里的代码在干什么;文档通常也是相对独立于代码的。嵌入了代码的文档或嵌入了文档的代码对于一般读者来说都不易读,所以总而言之,文学化编程本来是一个坏主意。 |
| 35 | +文学化编程本来是为了编程和写文档两个目的而生的,想法看起来很好,但同时完成这两件事情绝非易事。编程通常就是代码加注释,大家都熟悉这个套路,没有人在代码之间长篇大论写文档来解释这里的代码在干什么;文档通常也是相对独立于代码的。嵌入了代码的文档或嵌入了文档的代码对于一般读者来说都不易读。这么看来,文学化编程并非一个很好的点子。 |
36 | 36 |
|
37 | | -然而,文学化编程范式却无意间影响了可重复的科学研究。它不适合写代码,但适合写数据分析报告。数据分析比纯写代码有趣,因为结果经常是图文并茂,但数据分析的过程往往是一团糟,这里就是文学化编程的想法能发力的地方。代码嵌入文档中的目的不是为了单纯的编译,而是为了出结果(图表等)。代码和文档结合之后,也便于动态控制输出结果[^literate-programming]。 |
| 37 | +然而,文学化编程范式却无意间影响了可重复的科学研究。它不适合单纯写代码,但非常适合写数据分析报告。虽然数据分析的过程往往是一团糟,但结果经常是图文并茂,这也就是文学化编程可以充分发挥实力的地方。代码嵌入文档中的目的并非为了单纯的编译,而是更好的展示分析结果(合理使用文字与图表等)。代码和文档结合之后,也便于动态的调整输出结果[^literate-programming]。 |
38 | 38 |
|
39 | 39 | [^literate-programming]: 上述两段文字摘自谢益辉博客 2014 年 1 月 3 日的一篇文章,有删改。原文见:<https://yihui.org/cn/2014/01/literate-programming/>。 |
40 | 40 |
|
41 | 41 | ## 以案例形式初识 R Markdown {#rmarkdown-example} |
42 | 42 |
|
| 43 | +**rmarkdown** [@R-rmarkdown] 一个是 R 语言的扩展包,提供了以 `.Rmd` 文件格式为中心的数据科学写作框架。它的核心思想是创作包含文本和代码的动态文档,让数据与叙事并进,最终提供易重复,易共享的数据分析环境。 |
| 44 | + |
| 45 | +本章从 R Markdown 的应用开始,遴选了不同场合 R Markdown 的用例和解决的问题。包括: |
| 46 | + |
| 47 | +- 撰写数据分析报告 |
| 48 | + |
| 49 | +- 用交互笔记本记录学习过程 |
| 50 | + |
| 51 | +- 发表电子书和长篇文档 |
| 52 | + |
| 53 | +- 设计数据驱动的个人简历 |
| 54 | + |
| 55 | +- 建立个人网站和博客 |
| 56 | + |
| 57 | +- 创作演示文档和交互报表。 |
| 58 | + |
| 59 | +- 用动态文档自动化报告发布 |
| 60 | + |
| 61 | +最后,本书回顾了 R Markdown 的技术渊源和发展。 |
| 62 | + |
43 | 63 | ### 数据分析报告 {#example-report} |
44 | 64 |
|
| 65 | +从诞生开始,R 便是一门致力于交互式统计与数据分析的语言。而 R 用户的需求也自然而然地着重于数据分析与分析报告的生成,这也是 R Markdown 最常见的用途。 |
| 66 | + |
| 67 | +R Markdown 支持 HTML, PDF, Word, EPUB 等多种输出格式,以及与各种输出格式配套且开箱即用的主题系统。用户可以将工作重心放在内容创作上,即在写作完成后只需一键输出,让 R Markdown 的自动化的编译机制代劳形式上的调整。例如,只需要几行代码,同一个 R Markdown 文档就可以生成图 \@ref(fig:three-output-demo) 中 HTML,PDF 和 Word 三种格式的文档。 |
| 68 | + |
| 69 | +```{r three-output-demo, fig.show = "hold", fig.align='default', out.width = "33%", echo = FALSE, fig.cap = "同一个 R Markdown 文档可以生成多种格式的输出"} |
| 70 | +knitr::include_graphics(paste0("examples/", c("three-output-html.png", "three-output-pdf.png", "three-output-word.jpg"))) |
| 71 | +``` |
| 72 | + |
| 73 | +除了多样的输出格式外,R Markdown 驱动的数据分析报告还解决了很多数据分析报告写作中的痛点: |
| 74 | + |
| 75 | +- 之前在使用 R 或者其他数据分析工具时,经常需要在 Word 里写结论,在脚本里敲代码,在图表区生成图,将它们复制粘贴到一起后,还要操心格式问题,有没有什么自动化的解决方案? |
| 76 | + |
| 77 | +- 我的日常工作包含大量重复性高的数据分析与产出,如何创作一篇参数化、可复用的文档模板,从此可以在更新数据后系统性的产出结论与图表? |
| 78 | + |
| 79 | +- 如何确保分析过程和结论是可复现的,即别人是否能利用同样的数据得到相同的结论? |
| 80 | + |
| 81 | +- 我不了解网页开发,如何在报告中插入可交互的图表和网页元素? |
| 82 | + |
| 83 | +同时,R Markdown 还为 R 语言之外的几十种编程语言提供了一定程度的支持,例如 Python,C++,Julia,Bash,SQL 等,这意味着用户可以在一篇报告中混用多种编程语言。例如,Python 是数据分析的另一大利器,在 **reticulate** 包 [@R-reticulate] 的帮助下,用户可以在 R Markdown 文档中流畅地并用 Python 和 R[^knitr-python-install]。在下面的例子中,本书先用 R 导入数据并作数据预处理,随后将 R 中的数据传递到 Python 环境中,生成 **pandas** 格式的 `DataFrame`, 并用 **seaborn** 包进行可视化。 |
| 84 | + |
| 85 | +[^knitr-python-install]: 用户在安装好 reticulate 包后需使用 `py_install("package")` 样式的代码来安装需要使用的 Python 程序包。如 `py_install("pandas")` 代码会安装 pandas 程序包。在文中的例子中,用户需要提前安装 pandas 和 seaborn 两个 Python 程序包,否则 R 会报错。 |
| 86 | + |
| 87 | +```{r} |
| 88 | +# R 部分: 导入和预处理美国各州犯罪数据 |
| 89 | +arrests <- datasets::USArrests |
| 90 | +arrests$State <- rownames(arrests) |
| 91 | +``` |
| 92 | + |
| 93 | + |
| 94 | +```{python, eval = FALSE} |
| 95 | +# Python 部分,导入 R 数据,提取谋杀率最高的 10 个州,并用 seaborn 包作柱形图 |
| 96 | +import pandas as pd |
| 97 | +import seaborn as sns |
| 98 | +
|
| 99 | +top_states = r.arrests\ |
| 100 | + .sort_values(["Murder"], ascending = False)\ |
| 101 | + .head(10) |
| 102 | +
|
| 103 | +sns.barplot(x = "Murder", y = "State", data = top_states) |
| 104 | +``` |
| 105 | + |
| 106 | +本书在第 \@ref(basic-other-languages) 节给出了在数据分析项目中混用 SQL,R 和 Python 的例子,并在第 \@ref(other-languages) 章中详细讨论了如何在 R Markdown 中结合其他编程语言。 |
| 107 | + |
| 108 | +当需要创作更大篇幅的文档甚至书籍时,有可能创作者不希望仅使用一篇 R Markdown 文档组织全部内容。**bookdown** 包 [@R-bookdown] 可以让用户将内容分散到多个 R Markdown 文档中,在编译时自动合成各文档的内容,并提供更适于书籍和在线文档的输出格式。此外,bookdown 对 R Markdown 的扩展还包括支持交叉引用,定理和公式环境,文献引用等。bookdown 的输出结果非常适合用于制作在线教材与讲义,例如北京大学李东风老师的[《R 语言教程》](https://www.math.pku.edu.cn/teachers/lidf/docs/Rbook/html/_Rbook/index.html),本书的在线版本也是用 bookdown 生成的。[bookdown.org](https://bookdown.org/home/) 列出了更多 bookdown 制作的在线书籍。 |
| 109 | + |
| 110 | +```{r bookdown-chinese-demo, fig.cap = "使用 bookdown 制作的中文图书范例, 一个章节对应一个 R Markdown 文档", echo = FALSE} |
| 111 | +knitr::include_graphics("images/01-bookdown-chinese.png") |
| 112 | +``` |
| 113 | + |
| 114 | +在学术报告或论文的撰写中,用户可能对格式有更细致的要求。越来越多的包提供了易用的 R Markdown 模板,为用户免去了繁杂的手动格式调整,编译后即可直接投稿。**rticles** 包 [@R-rticles] 提供了很多期刊和出版商的模板文档,例如 R Journals 和 Journals of Statistical Software。中文用户可能较常用 rticles 提供的 CTeX 输出格式,它让 R Markdown 输出的 PDF 可以正常显示中文字符。 |
| 115 | + |
| 116 | + |
45 | 117 | ### 交互笔记本 {#example-note} |
46 | 118 |
|
| 119 | +使用 R Markdown 及 bookdown 创作的文档支持运行代码,创作格式化长篇文本,插入 LaTeX 数学公式,且支持交叉引用和参考文献。因此统计,数据科学,计算机科学乃至社会科学等领域的学生,研究人员和爱好者们时常使用 R Markdown 为首选的笔记本格式。 |
| 120 | + |
| 121 | +```{r notebook-preview, echo = FALSE, fig.cap = "RStudio 支持实时预览 R Markdown 笔记本格式"} |
| 122 | +knitr::include_graphics("images/01-notebook-preview.png") |
| 123 | +``` |
| 124 | + |
| 125 | +如图 \@ref(fig:notebook-preview) 所示,R Markdown 笔记本支持插入代码,保存代码块的运行结果,添加格式化文本和批注等功能。R Markdown 的交互特性让学习记录变得十分自然:用户通常添加一段代码,运行后得到预料之中/之外的可视化结果,随后在下面的多个代码块中反复调整某个参数,直至得到想要的结果。在此过程中用户常常会发现更多相关的包或代码,写下对不同代码的总结,并链接网络上他人的用例,甚至开始撰写自动化脚本。最后,用户可以一键生成包含了这一系列思考和学习过程的 HTML 或 PDF 精美文档。图 \@ref(fig:notebook-preview) 是一个 PDF 笔记文档示例。 |
| 126 | + |
| 127 | +```{r notebook-preview2, echo = FALSE, fig.cap = "用 R Markdown 生成 PDF 学习笔记"} |
| 128 | +knitr::include_graphics("images/01-notebook-preview2.jpg") |
| 129 | +``` |
| 130 | + |
| 131 | +即便用户的工作不涉及数据分析或代码运行,使用 R Markdown 作为笔记软件也有许多好处。用户可以流畅的使用版本控制工具,管理笔记依赖的数据,图片的资源等。用户可以将长篇笔记拆分为多个源文档,用纯文本文件管理输出设置,形成清晰的项目结构。[RPubs](https://rpubs.com/) 和 [bookdown](https://bookdown.org/) 等平台提供了免费的 R Markdown 文档发布服务,以此来发布自己的笔记与心得,便利他人和未来的自己。 |
| 132 | + |
47 | 133 | ### 个人简历 {#example-cv} |
48 | 134 |
|
| 135 | +一些 R Markdown 扩展包提供了适用于个人简历(resume, CV)的输出格式。例如基于 **pagedown** 包 [@R-pagedown] 的[简历模板](https://pagedown.rbind.io/html-resume),和 [vitae](https://pkg.mitchelloharawild.com/vitae/) 提供的 CV 模板。 |
| 136 | + |
| 137 | +除了提供模板外, R Markdown 还能使简历变得更加以“数据驱动 (Data driven)”。[datadrivencv](http://nickstrayer.me/datadrivencv/) 包把简历中的各项教育背景,工作经历和项目经验等视作电子表格中的一条记录,用户仅需维护这张数据表,并由 R Markdown 来负责简历的格式。用户还可以插入 R 代码生成的交互图表,使简历可以“动”起来。图 \@ref(fig:ns-cv) 是 datadrivencv 包的作者 Nick Strayer 结合 **pagedown** 包制作的个人简历。 |
| 138 | + |
| 139 | +```{r ns-cv, echo = FALSE, fig.cap = "(ref:ns-cv)"} |
| 140 | +knitr::include_graphics("images/01-resume.png") |
| 141 | +``` |
| 142 | + |
| 143 | +(ref:ns-cv) [Nick Strayer](http://nickstrayer.me/)用 datadrivencv 和 pagedown 包制作的 CV 示意 |
| 144 | + |
| 145 | + |
49 | 146 | ### 网站 {#example-resume} |
50 | 147 |
|
| 148 | +原生的 R Markdown 包内置了生成简单静态网站的功能。简单来说,用户在一个 `.yml` 文件中自由定义网站的标题,导航栏,页面结构等元数据,而后创建数个 R Markdown 文档并在其中填充内容。最后,内置的站点生成器将每个 R Markdown 文档渲染为一个 HTML 页面输出。 |
| 149 | + |
| 150 | +```{r, echo = FALSE, fig.cap = "用 R Markdown 创建网站"} |
| 151 | +knitr::include_graphics("images/01-rmarkdown-wbesite.png") |
| 152 | +``` |
| 153 | + |
| 154 | +为满足创建更加复杂且精美的网站的需求,**blogdown** 包使用了第三方的开源静态网站生成器:Hugo。Hugo 是目前速度最快,最受欢迎的静态网站生成器之一。一般用户通常需要用命令行的方式与之交互。不过作为幸福的 R 用户,读者可以在 R Markdown 文档中直接进行写作,随后调用 blogdown 中封装好的函数操作 Hugo 的编译功能,最后生成网站。 |
| 155 | + |
| 156 | +```{r hugo-themes, fig.cap = "blogdown 包可以使用 Hugo 主题", echo = FALSE} |
| 157 | +knitr::include_graphics("images/01-hugo-themes.png") |
| 158 | +``` |
| 159 | + |
| 160 | +包如其名,对个人用户来说,blogdown 特别适合制作博客类的个人网站。读者可以展示个人项目,存放简历,归档学习笔记和心得,求职时为自己添加一份切实的筹码。[rbind](https://support.rbind.io/about/) 还提供了免费的域名服务。除此之外,用户还可以用 blogdown 制作其他类型的网站,例如软件文档,课程主页等。Hugo 的[主题列表](https://themes.gohugo.io/) 列出了丰富的网站模板,其中大部分可以在 blogdown 中一键生成。最后,本书提供几个不同主题的 blogdown 网站样例以供读者参考: |
| 161 | + |
| 162 | +- Rob J Hyndman 的[个人网站](https://robjhyndman.com/) |
| 163 | + |
| 164 | +- Alison Hill 的[个人网站](https://alison.rbind.io/) |
| 165 | + |
| 166 | +-《现代统计图形》的[图书主页](https://msg2020.pzhao.org/) |
| 167 | + |
| 168 | + |
| 169 | + |
51 | 170 | ### 幻灯片 {#example-ppt} |
52 | 171 |
|
| 172 | + |
| 173 | +R Markdown 和扩展包支持输出多种常见的幻灯片格式,例如 PowerPoint,Beamer,isoslides 和 Slidy 等。除此之外,本书还特别推荐 **xaringan** [@R-xaringan] 包。此包基于 JavaScript 中的 remark.js 库设计了灵活的输出方案。对 CSS 和 JavaScript 语法有一定了解的读者还可以基于默认模板自定义出复杂的演示文档。**xaringanExtra** 包为 xaringan 幻灯片提供了更多有趣的增强插件。读者可以在 <https://slides.yihui.org/xaringan/#1> 和 <https://pkg.garrickadenbuie.com/xaringanExtra/#/> 看到更多示例。 |
| 174 | + |
| 175 | +```{r xaringan, out.width="49%", fig.show = "hold", fig.cap = "xaringan 提供的幻灯片模板示意", echo = FALSE, fig.align="default"} |
| 176 | +knitr::include_graphics(c("images/01-xaringan1.png", "images/01-xaringan2.png")) |
| 177 | +``` |
| 178 | + |
| 179 | + |
53 | 180 | ### 交互报表 {#example-forms} |
54 | 181 |
|
| 182 | + |
| 183 | +报表是一种常见的商业报告形式,用 R Markdown 开发报表不仅可以将数据分析过程与商业结论结合地在一起,还能帮助用户充分发挥 R 语言强大的可视化功能。基于 HTML 格式的 **flexdashboard**[@R-flexdashboard] 包提供了这类报表的模板,还内置了一些常用报表元素的 HTML 组件, 例如指标盒,增长仪表,导航栏等。 |
| 184 | + |
| 185 | +```{r flexdashboard, echo = FALSE, fig.cap = "用 flexdashboard 包制作的仪表板"} |
| 186 | +knitr::include_graphics("images/01-flexdashboard.png") |
| 187 | +``` |
| 188 | + |
| 189 | +除去开发上的便捷易用外,R Markdown 生态还提供了丰富的测试,部署,自动化和持续集成功能,加速并确保用户的数据产品投入生产的效率。 |
| 190 | + |
| 191 | + |
55 | 192 | ### 动态文档 {#example-dynamic} |
| 193 | + |
| 194 | +除了直接的界面写作外,用户还可以通过调用输出 API `rmarkdown::render()` 来生成结果文档,并可以控制 R Markdown 输出时使用的数据源。这让 R Markdown 的自动化批量生产成为可能。一个典型场景是:数据库每日更新全国销售数据,而数据分析师用自动化脚本控制 R Markdown 来根据不同地区与职务来生成多个更有针对性的报表。这样的动态文档是通过参数实现的,也就是说用户在源文档中使用参数的名字,而不是具体的数据来进行定义。随后在脚本中通过 `rmarkdown::render()` 动态来传入当日的数据。 |
| 195 | + |
| 196 | + |
| 197 | +```{r dynamic-document, fig.cap = "根据 Shiny 输入动态生成 R Markdown 文档", echo = FALSE} |
| 198 | +knitr::include_graphics("images/01-dynamic-document.jpg") |
| 199 | +``` |
| 200 | + |
| 201 | +图 \@ref(fig:dynamic-document) 是一个结合 Shiny 包动态生成 R Markdown 输出文档的例子。R Markdown 允许用户上传某个数据集,挑选需要数据清理的维度,随后把清理结果动态地传递给 `rmarkdonw::render()`,从而生成右侧截图所示的输出文档。 |
| 202 | + |
| 203 | +## R Markdown 的渊源与发展 |
| 204 | + |
| 205 | +本节回顾了 R Markdown 在技术工具层面的渊源和它的发展历史。喜欢直接进入实际操作部分的读者可以跳过本节,从第 \@ref(installation) 章开始。如果读者已经掌握了 R Markdown 的基础知识,书写过一些 R Markdown 的文档, 也可以直接开始阅读第 \@ref(document-elements) 章,了解如何定制不同输出格式及更多细节。 |
| 206 | + |
| 207 | + |
0 commit comments