第 2 章 从 R Markdown 到 HTML 文档
因为 Markdown 技术在设计之初的输出格式就是 HTML,所以 HTML 文档不仅仅是最常用 R Markdown 输出格式,同时也拥有最丰富的功能。
前面提过,R Markdown 生成 HTML 文档的过程有一个中间步骤,就是 Markdown + HTML 模板。HTML 模板包括预定义的文档结构、 CSS 样式表和 JavaScript 动态网页功能等,所以最终渲染得到的 HTML 文档的一些功能可能依赖于特定 HTML 模板才能实现。
默认情况下,R Markdown 的 HTML 文档使用 rmarkdown::html_document
模板。除此之外, rmarkdown
之外的其它软件包也提供了各种不同类型的 HTML 模板文件(如 bookdown::html_document2
,pagedown::html_paged
等)。因此,在这一部分我们先从 R Markdown 到 html_document
文档开始。
要创建一个 html_document
,只需要在 R Markdown 的开头加入 YAML 格式的元数据。
---
title: "文档标题"
author: "作者"
date: "创建时间"
output: html_document
---
在元数据中,还可以加入各种各样设置,从而改变文档的格式。
2.1 目录和标题
2.1.1 显示目录
在开头的元数据中,使用 toc: true
可以打开文档的目录。目录会从 HTML 的标题自动生成,目录的层级由 toc_depth
的值确定(默认为 3)。例如:
---
title: "标题"
output:
html_document:
toc: true
toc_depth: 2
---
在上面的例子中,目录中将包含 HTML 文档中的一级标题和二级标题。
2.1.2 悬停目录
当把 toc_float
设为 true
的时候,目录会在侧边栏悬停。这样你就可以随时借助目录在一篇比较长的文档中跳转了。
---
title: "标题"
output:
html_document:
toc: true
toc_float: true
---
通过改变 toc_float
的选项,可以控制侧边栏的折叠和动画。其中:
collapsed
(默认为true
)控制文档第一次打开时目录是否被折叠。如果为true
则只显示高级别的标题(二级标题及以上元素);smooth_scroll
(默认为true
)控制页面滚动时,标题是否会随之变化。
---
title: "标题"
output:
html_document:
toc: true
toc_float:
collapsed: false
smooth_scroll: false
---
2.1.3 显示标题编号
使用 number_sections
可以在标题开头加上编号。一级标题编号为 “1 一级标题”,二级标题为“1.1 二级标题”。
---
title: "标题"
output:
html_document:
toc: true
number_sections: true
---
2.1.4 使用标签页
在 HTML 文档中使用标签页非常简单,只需要在标题后附加 {.tabset}
标签即可。
## Quarterly Results {.tabset}
### By Product
(tab content)
### By Region
(tab content)
在此基础上,添加相关的额外标签还可以控制标签的样式和行为。其中:
.tabset-fade
将为标签切换过程加入淡入淡出的动画效果;.tabset-pills
将为标签文字加上预设的 “pill”样式(图 2.1)。
2.2 主题和样式
2.2.1 可选主题
为了满足颜值党的差异化需求,html_document
自带了多个不同的主题。这些主题来自于 Bootswatch。可用的主题名称包括 default,cerulean,journal,flatly,darkly,readable,spacelab,united,cosmo,lumen,paper,sandstone,simplex 和 yeti 等等。
主题使用 theme
参数来指定,例如:
---
title: "标题"
output:
html_document:
theme: united
---
如果设置为 theme: null
,那么将不会应用任何主题,此时你可以通过指定自定义的 CSS 样式表来进行格式化。
---
title: "标题"
output:
html_document:
theme: null
css: style.css
---
2.2.2 代码高亮
代码高亮也有可选的多种样式,包括 default
,tango
,pygments
,kate
,monchrome
,espresso
,zenburn
,haddock
,breezedark
和 textmate
等。
在元数据中,使用 highlight
参数指定代码高亮样式。
---
title: "标题"
output:
html_document:
highlight: tango
---
跟上面的 theme
属性一样,highlight
也可以设为 null
,这样的话代码将不显示高亮。
2.2.3 自定义样式表
不论有没有应用主题和代码高亮,你都可以使用 css
参数指定附加样式表。附加样式表中的定义可以被应用到特定元素上去。
---
title: "标题"
output:
html_document:
theme: null
highlight: null
css: styles.css
---
在 styley.css
中,假设我们定义了两个新样式如下:
#nextsteps {
color: blue;
}
.emphasized {
font-size: 1.2em;
}
则可以通过下列方式应用这个样式:
## 这里强调一下下一步 {#nextsteps .emphasized}
如果要应用多个 CSS 配置文件,可以这样写:
output:
html_document:
css: ["style-1.css", "style-2.css"]
2.2.4 通过 CSS 代码块定义样式
除了像上面那样导入一个预定义的样式表,你还可以直接在 R Markdown 中添加新的 CSS 定义。这种方法非常适合于个别定义。如果你的 CSS 代码很多,或者想要在其它 R Markdown 中使用同样的定义,可能放在自定义的样式表文件中更加合适(参见 2.2.3)。
---
title: "对代码块使用自定义的 CSS 样式"
output: html_document
---
`watch-out`。
首先定义一个新样式
```{css, echo=FALSE}
.watch-out {
background-color: lightpink;
border: 3px solid red;
font-weight: bold;
}
```
`class.source` 可以将新样式指定给这一区块。
使用代码块属性
```{r class.source="watch-out"}
mtcars[1:5, "mpg"]
```
2.2.5 使用丰富多样的内置 CSS 样式
大多数时候,你并不需要自己定义 CSS 样式,因为 HTML 内置的主题中已经包含了丰富且高级的内置样式。
默认情况下,R Markdown 输出的 HTML 文档中,已经内嵌了 Bootstrap 框架,因此可以使用一系列预定义的 CSS 样式。其中,可用的背景样式就包括 "bg-primary"
,"bg-success"
,"bg-info"
,"bg-warning"
和 "bg-danger"
等。
只需要像上面那样,在代码块属性中使用 class.source
标签,就可以应用这些预定义的 CSS 样式。
---
title: 改变代码块的样式
output: html_document
---
当你对一个数据框取子集的时候,其输出跟选取的列的数目有关。
如果选取了 2 个以上的列,则输出仍然是一个数据库;
如果选取了 1 个列,则输出的结果将会是一个向量。`bg-danger` 和 `bg-warning` 的样式。
因此,我们对这个操作应用了
```{r class.source="bg-danger", class.output="bg-warning"}
mtcars[1:5, "mpg"]
```
`drop = FALSE` 参数。
为了确保始终能够得到数据框,则需要添加 `bg-success` 样式。
为了显示这个结果,我们应用了
```{r df-drop-ok, class.source="bg-success"}
mtcars[1:5, "mpg", drop = FALSE]
```
以上内容生成 HTML 文档后的样子如下图所示:
2.2.6 代码折叠
当 Knitr 的代码块参数 echo = TRUE
时,R 代码将会输出到最终生成的文档中。
如果你不需要显示源代码,可以直接设为 echo = FALSE
。
如果你既想要保留代码但又让其默认不显示,
则可以通过 code_folding: hide
参数来实现。
---
title: "Habits"
output:
html_document:
code_folding: hide
---
code_folding: hide
将折叠所有的代码块,用户可以通过点击来查看折叠的代码。如果想让部分代码块在一开始就显示,则可以在代码块选项中使用 class.source = 'fold-show'
。
---
title: "代码的折叠和显示"
output:
html_document:
code_folding: hide
---
```{r}
1 # 折叠的
```
```{r class.source = 'fold-show'}
2 # 显示的
```
```{r}
3 # 还是折叠的
```
这种操作也可以反向进行,如下配置即可:
---
output:
html_document:
code_folding: show
---
```{r}
1 # 代码默认是显示的
```
```{r class.source = 'fold-hide'}
2 # 这一块代码将被折叠
```
如图 2.4 所示,在页面和每一个代码块的右上方有一个按钮。页面右上方的按钮可以控制全部代码块的显示和隐藏,代码块右上方的按钮则可以控制对应代码块的显示和隐藏。
2.2.7 设置代码块内容可滚动
如果你想限制代码块的高度,特别是代码执行过程中输出内容的高度,还可以从相应内容的 CSS 样式上着手,即通过 class-output
和 class-source
来定义内容的高度。
下面是一个例子:
---
title: 可以滚动的代码和输出
output: html_document
---
```{css, echo=FALSE}
pre {
max-height: 300px;
overflow-y: auto;
}
pre[class] {
max-height: 100px;
}
```
我们首先定义了上述 CSS 规则,用来限制代码块的高度。
```{r}
# 假如这里有 N 多行的代码
if (1 + 1 == 2) {
# 然后再打印一个非常长的数据
print(mtcars)
# 如果不够长的话,再加上这行注释可能就够了
}
```
`scroll-100`,以用来限制代码块的输出高度为 100 像素。
现在添加一个新的 CSS 类 `class.output` 参数。
然后,将这个类赋值给代码块中的
```{css, echo=FALSE}
.scroll-100 {
max-height: 100px;
overflow-y: auto;
background-color: inherit;
}
```
```{r, class.output="scroll-100"}
print(mtcars)
```
因为代码块是位于 <pre class="sourcecode">
标签内的,所以 pre[class]
操作符将限制代码块的高度为不超过 100 像素。 而将 class.output
设为 scroll-100
则限制输出部分的高度为不超过 100 像素。
最终效果如下图所示:
2.3 图片和数据框
2.3.1 设置图片的属性
下列参数可以调整生成的 HTML 文档中图片的属性:
fig_width
,fig_height
指定图片显示时的宽和高(默认为 7 × 5,单位英寸)。fig_retina
开启视网膜屏幕优化(默认为 2,设为null
时关闭优化)。fig_caption
控制是否渲染图注。dev
设置图片输出设备,默认为png
。你可以设置多个图片输出设备。
---
title: "标题"
output:
html_document:
fig_width: 7
fig_height: 6
fig_caption: true
dev: c("png","pdf")
---
在代码框属性中,你仍然可以通过 fig.height
,fig.width
,fig.asp
等参数来指定生成图片的高度、宽度和宽高比。
2.3.2 插入外源图片
虽然在 R Markdown 中可以使用 Markdown 语法和 HTML 语法插入图片,但是我们推荐使用 knitr::include_graphics()
来插入外源图片。例如:
```{r}
knitr::include_graphics("https://r-project.org/Rlogo.png")
```
使用这种方法插入外源图片,可以方便的添加图注(使用 fig.cap
参数),以及设置图片的宽高等属性。
2.3.3 输出数据框
通过 df_print
参数,你可以调整数据框的输出格式。可用的参数如表 2.1 所示。
取值 | 说明 |
---|---|
default | 调用 print.data.frame 的通用方法 |
kable | 使用 knitr::kable 函数 |
tibble | 使用 tibble::print.tbl_df 函数 |
paged | 使用 rmarkdown::paged_table 来创建一个分页的表格 |
2.3.4 分页打印数据框时的附加参数
若把 df_print
参数设为 paged
,数据框将支持行列的分页,效果如图 2.7 所示。
---
title: "Motor Trend Car Road Tests"
output:
html_document:
df_print: paged
---
```{r}
mtcars
```
表 2.2 列出了这种情况下可以使用的附加参数。
参数名 | 说明 |
---|---|
max.print | 显示的总行数 |
rows.print | 一页显示的行数 |
cols.print | 一页显示的列数 |
cols.min.print | 最少显示几列 |
pages.print | 下方显示几个页面导航的链接 |
paged.print | 设为 FALSE 则不输出分页的表格 |
rownames.print | 设为 FALSE 时不显示行的名称 |
这些参数可以在代码块中使用。
```{r cols.print=3, rows.print=3}
mtcars
```
2.4 组件和内容
2.4.1 理解 HTML 文档的依赖关系
前面已经提过,R Markdown 输出的 HTML 文档时,依赖于软件包提供的 HTML 模板。 实际上,HTML 文档的样式和功能很大程度上依赖于一些 CSS 样式和 JavaScript 库的支持。 这里面包括 Bootstrap,JQuery 等优秀的开源项目。
默认情况下,R Markdown 输出的 HTML 文档是一个单一的 .html
文件。这是由 self_contained: true
控制的。.html
文件中,会使用 data:
存储包括 JavaScript、CSS、图片和视频在内的全部资料。这样的一个封装,使得用户可以像分享 PDF 或 Word 文档那样分享文件,同时享受超链接、动态效果等一系列 HTML 特性。
如果你指定 self_contained: false
,那么 HTML 文档将会将自身的依赖文件单独存放。
---
title: "Habits"
output:
html_document:
self_contained: false
---
默认情况下,在 .html
文件同一目录会生成同名的 _files
文件夹,存放相应的依赖文件。下面是默认情况下一份 HTML 文档所包含的依赖文件。
+---anchor-sections-1.0
+---bootstrap-3.3.5
| +---css
| | \---fonts
| +---fonts
| +---js
| \---shim
+---header-attrs-2.5
+---highlightjs-9.12.0
+---jquery-1.11.3
\---navigation-1.1
库文件夹的位置可以由 lib_dir: xxx
指定,例如:
---
title: "Habits"
output:
html_document:
self_contained: false
lib_dir: libs
---
依赖文件的内容会随配置变化,例如当在 YAML 配置中加入 df_print: paged
之后,依赖文件中会多一个 pagedtable-1.1
的子文件夹出来。
显然,存放依赖的子文件夹依据 库名 + 版本号 的规则命名。要弄清楚库文件的全部特性,可能要对库本身有相当的理解才行。而 R Markdown 则是把最常用的功能整合提供给了我们。
当文档的内容比较少,同时又有多个类似的文档的时候,库文件所占的存储空间可能比你自己编写的内容还要大得多。这种情况下,将库文件统一存储在指定的 libs
文件夹,可以实现库文件公用。
另外,有些库文件不常用,或者文件太大,还可以通过库文件的服务器调用。例如下面要讲的 MathJax 库。
2.4.2 MathJax 库的配置
HTML 文档需要 MathJax 脚本来渲染 Latex 和 MathML 公式,调用 MathJax 的方式则可以通过 mathjax
参数来调整。
"default"
:默认配置,会通过 HTTPS 链接从 RStudio 的 CDN 网络服务器上调用;"local"
:与self_contained: false
联合使用时,会将 MathJax 库文件保存在本地目录中;设置一个 URL 链接,指向可用的 MathJax 库文件地址;
null
:完全不使用 MathJax。
例如,使用 MathJax 的本地拷贝可以如下配置:
---
title: "Habits"
output:
html_document:
mathjax: local
self_contained: false
---
为 MathJax 配置一个新的可用来源。
---
title: "Habits"
output:
html_document:
mathjax: "http://example.com/MathJax.js"
---
不使用 MathJax。
---
title: "Habits"
output:
html_document:
mathjax: null
---
2.4.3 是否保留 Markdown
knitr 处理 R Markdown 文件时,会先生成一个 Markdown 文件(*.md
),随后再由 Pandoc 转换成 HTML 文档。如果需要保留这个 Markdown 文件,可以使用 keep_md
选项。
---
title: "Habits"
output:
html_document:
keep_md: true
---
2.4.4 使用自定义的 HTML 模板
使用 template
选项,可以配置 Pandoc 转换时使用的模板。
---
title: "Habits"
output:
html_document:
template: another_template.html
---
Pandoc 模板遵循特定的格式,有关的进一步信息可以在 Pandoc 模板 页面获得。
下面是一个 HTML 模板的示例:
<html>
<head>
<title>$title$</title>
$for(css)$<link rel="stylesheet" href="$css$" type="text/css" />
$endfor$</head>
<body>
$body$</body>
</html>
这其中包括一些变量,如 $title$
,$body$
等。这些变量由 Pandoc 定义,完整的变量列表参见这里。
这样的 HTML 模板使得高度定制化的输出成为可能。例如,你可以在 <head>
区域加入任意的 CSS 样式, JavaScript 代码,以及其它的开源库。 另外,还可以定义一些新变量来控制文档的格式化。例如,定义一个布尔值 draft
来确定文档是一个草稿还是最终版本。
<head>
<style type="text/css">
.logo {
float: right;
}</style>
</head>
<body>
<div class="logo">
$if(draft)$<!-- use draft.png to show that this is a draft -->
<img src="images/draft.png" alt="Draft mode" />
$else$<!-- insert the formal logo if this is final -->
<img src="images/logo.png" alt="Final version" />
$endif$</div>
$body$</body>
draft
在 YAML 元数据中赋值。
---
title: "An Important Report"
draft: true
output:
html_document:
template: my-template.html
---
说明:rmarkdown 软件包默认使用自带的 HTML 模板,一些方面与 Pandoc 默认的模板存在差异。如果有 template: null
的话,则 Pandoc 的模板会被使用。
2.4.5 包含其它文件
使用 includes
选项,可以在 HTML 文档的不同位置嵌入其它的 HTML 格式内容。可选的位置包括在 HTML 文档的 header
、body
前/后等。
---
title: "Habits"
output:
html_document:
includes:
in_header: header.html
before_body: doc_prefix.html
after_body: doc_suffix.html
---
这种方式可以很方便的为文档加入一些第三方功能和公用的元件。例如在 in_header
中导入预定义的 CSS 样式表和 Javascript 脚本,在 before_body
中加入导航栏,在 after_body
中加入一个底栏等。
下面的例子中,即添加了一个简单的底栏。将其中内容保存到一个 HTML 文件中,放在 after_body
后面即可。
<div class="footer">Copyright © R Markdown 指南 2021</div>
除了以上几种常用的位置,你还可以在任意地方插入 HTML 内容。实现这一功能的途径至少有两种。
一是使用 htmltools::includeHTML()
方法:
```{r, echo=FALSE, results='asis'}
htmltools::includeHTML('file.html')
```
二是使用 xfun::file_string()
方法:
```{r, echo=FALSE, results='asis'}
xfun::file_string('file.html')
```
需要注意的是,导入的 HTML 文件必须是 HTML 片段,而不能是一个完整的 HTML 文档。完整的 HTML 文件中有 <html>
标签,解析时会造成错误。比如下面就是一个无效的例子:
<html>
<head> </head>
<body>
Parent HTML file.
<!-- htmltools::includeHTML() below -->
<html>
<head> </head>
<body>
Child HTML file.</body>
</html>
<!-- included above -->
</body>
</html>
2.4.6 生成 HTML 片段
HTML 片段是一个不完整的 HTML 文件,这种片段适合用于嵌入其它的网页或者内容管理系统(如博客)中。HTML 片段也不自带主题和代码高亮,而通过继承的方式使用其嵌入文件或系统的设置。这样的 HTML 片段,也非常适合用于包含在其它文件中(参见 2.4.5)。
---
output: html_fragment
---
下面是一个 HTML 片段的例子:
<p>HTML 片段可以有标题和代码区域,但是不包含任何 CSS 定义。</p>
<pre class="r"><code>head(mtcars)</code></pre>
<pre><code>## mpg cyl disp hp drat wt qsec vs am gear carb
## Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
## Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
## Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
## Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
## Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2</code></pre> ## Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
将这些内容保存为一个 HTML 文件,导入此处,则效果如下所示:
HTML 片段可以有标题和代码区域,但是不包含任何 CSS 定义。
head(mtcars)
## mpg cyl disp hp drat wt qsec vs am gear carb
## Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
## Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
## Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
## Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
## Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
## Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
HTML 片段的内容到此结束。
2.4.7 使用自定义的浏览器图标
通过在 in_header
部位导入 HTML 内容,可以为 HTML 文档设定一个自定义的浏览器图标。
将下面的内容保存到一个名为 header.html
的文档中。
<link rel="shortcut icon" href="{path to favicon file}" />
然后通过 includes
导入文件的内容,则可以改变浏览当前文档时的浏览器图标。
output:
html_document:
includes:
in_header: header.html
2.4.8 共用 YAML 配置文件
当前目录中的 _output.yml
文件是一个配置文件,其中的设置可以被目录下所有的 R Markdown 文档公用。需要注意的是,该文件中的内容不需要使用---
和 output
标签。
如下所示:
html_document:
self_contained: false
theme: united
highlight: textmate
而不应该写成下面这种样子:
---
output:
html_document:
self_contained: false
theme: united
highlight: textmate
---
2.4.9 嵌入 Rmd 源文件
当你分享一个 R Markdown 生成的 HTML 文档给他人的时候,对方可能还想找你索取 .Rmd
源文件。在头文件中配置 code_download
参数可以在 HTML 文档中嵌入源文件。
output:
html_document:
code_download: true
打开 code_download
选项后,页面中会出现一个下载按钮,点击下载按钮即可获得源文件。
2.4.10 嵌入其它文件
嵌入 .Rmd
源文件可能还不足以重现 R Markdown 中结果,通常还会需要原始数据等其它内容。要将这些内容嵌入 HTML 文档中,也是很容易实现的。
这一功能通过 xfun 软件包(R-xfun?) 实现,依赖于 htmltools
和 mime
软件包。要使用这一功能,首先确保这两个软件包可用。
::pkg_load2(c('htmltools', 'mime')) xfun
现在,就可以随意嵌入各种文件了。
```{r echo=FALSE}
# 不但可以嵌入一个文件
xfun::embed_file('source.Rmd')
# 还可以嵌入多个文件
xfun::embed_files(c('source.Rmd', 'data.csv'))
# 甚至一个目录也不在话下
xfun::embed_dir('data/', text = 'Download full data')
```
不仅如此,你还可以编程技巧嵌入所需的文件。
# 嵌入当前目录下所有的 Rmd 和 csv 文件
::embed_files(list.files('.', '[.](Rmd|csv)$')) xfun
说明:当嵌入多个文件时,其工作原理是:首先将这些文件压缩成 Zip 格式,然后将 Zip 文件嵌入到 HTML 文档中。