第 5 章 使用 R Markdown 创建动态交互文档

5.1 Web 交互界面:Shiny 小程序

Shiny 小程序是由 R 驱动的交互式 Web 应用程序。基于 Shiny 的 Web 应用程序(Shiny App)用途十分广泛,功能也很强大,实现的方式也多种多样。本书主要介绍与 R Markdown 文档应用相关的一部分知识,欲了解更多请参见 Shiny 官网

Shiny 包是由 R 驱动的交互式 Web 应用程序。如果读者想从 R Markdown 文档中调用 Shiny 代码,只需在 YAML 元数据中添加 runtime: shiny 即可。由于 Shiny 构建 Web 应用程序功能十分强大,本书只介绍部分的 Shiny 知识。在阅读本章之前,如果不了解该包,可以先通过以下网站 https://shiny.rstudio.com 进行大致了解。

5.1.1 入门指南

在 YAML 元数据中加入 runtime: shiny,可以将任何基于 html 的 R Markdown 文档变成 Shiny 文档,例如:

---
title: "Shiny Document"
output: html_document
runtime: shiny
---

注意,R Markdown 文档的输出格式必须是 HTML 格式。也就是说,文档最后生成的是一个 Web 页面(*.html 文件)。

注意:html 格式,如 pdf_documentword_document 将不能与 Shiny 同时运行。另外,一些演示格式也是 HTML 格式,比如ioslides_presentationslidy_presentation,它们是可以与 Shiny 相结合的。

当然,也可以通过 RStudio 构建一个新的 Shiny 文档。步骤如下:File -> new File -> R Markdown 并选择 Shiny,具体见图 5.1

 在 RStudio 中创建一个新的 Shiny 文档。

图 5.1: 在 RStudio 中创建一个新的 Shiny 文档。

如果读者想在 RStudio 中运行 Shiny 文档,需要单击工具栏上的 “Run Document” 按钮(当 RStudio 检测到这是 Shiny 文档时,它会自动将 Knit 按钮替换为 Run Document)。如果读者没有使用 RStudio,或者想在 R 控制台运行文档进行故障排除,可以调用函数 rmarkdown::run() 并将文件名传递给它。

可以在文档中嵌入 Shiny 的输入和输出。如果输入发生变化时,输出将自动更新。例如,创建一个名称为 rows 的数字输入( numericInput ),然后在输出中通过 input$rows 引用其值:

numericInput("rows", "How many cars?", 5)

renderTable({
  head(cars, input$rows)
})
增加 Shiny 文档中表中的行数

图 5.2: 增加 Shiny 文档中表中的行数

在上面的示例中,输出代码包含在 renderTable() 中。 Shiny 中还有许多其他渲染功能,可用于图片、结果输出等。 下面使用 renderPlot() 输出可交互柱状图:

```{r, echo=FALSE, eval = FALSE}
sliderInput("bins", "Number of bins:", 30, min = 1, max = 50)

renderPlot({
  x    = faithful[, 2]  # Old Faithful Geyser data
  bins = seq(min(x), max(x), length.out = input$bins + 1)

  # 用指定的格子大小绘制可交互的直方图
  hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
```
Shiny 文档输出可交互的直方图

图 5.3: Shiny 文档输出可交互的直方图

5.1.2 Shiny 部署

如果在本地运行一个 Shiny 文档时,默认情况下它会使用本地 R 会话,并且只有运行的人能与该文档进行交互。如果希望与没有安装 R 的用户共享文档,或者不希望在本地运行文档,则必须将文档部署到服务器上,并共享文档的 URL。那么其他人只需要一个 web 浏览器就可以访问文档。

有两种方法可以部署 Shiny 文档。1. 使用 RStudio 提供的托管服务;2. 搭载到自己的服务器上。

5.1.2.1 ShinyApps.io

读者可以把 Shiny 文档发布到 ShinyApps (https://shinyapps.io) 进行托管。

需要满足以下两个条件:

  1. 在 ShinyApps 上注册账号。

  2. 安装了最新版本的 rsconnect 包。安装方式如下:

install.packages("rsconnect")

然后,对需要部署的 Shiny 文档所对应的工作目录中执行以下语句:

rsconnect::deployApp()

RStudio 用户也可以在运行 Shiny 应用后,点击窗口右上角的 Publish 按钮(图 5.4)。

部署 Shiny 文档到 ShinyApps.io. 上

图 5.4: 部署 Shiny 文档到 ShinyApps.io. 上

如果目录中有一个名为 index.Rmd 的文件,它将用作该目录的默认文档。如果想访问 Rmd 文档,则应在 URL 中指定 Rmd 文件的显式路径。

例如,部署到 ShinyApps 的 index.Rmd 的 URL 可以采用以下形式 https://example.shinyapps.io/appName/,而 test.Rmd 的 URL 可以采用以下形式 https://example.shinyapps.io/appName/test.Rmd

5.1.2.2 Shiny Server / RStudio Connect

除此之外,Shiny Server (https://www.rstudio.com/products/shiny/shiny-server/) 和 RStudio Connect(https://www.rstudio.com/products/connect/) 都可以发布 Shiny 文档,但是读者需要熟悉有关 Linux 的知识。由于该内容较为复杂与丰富,超出本书读者的需求,所以在此不做过多介绍。

5.1.3 嵌入 Shiny 应用程序

除了在 R Markdown 中嵌入单个 Shiny 的输入和输出外,还可以在文档中嵌入一个独立的 Shiny 应用程序。有两种方法可以做到这一点:

  1. 使用 shinyApp() 函数内部定义应用程序;

  2. 使用 shinyAppDir() 函数引用外部应用程序。

注意:这两个函数在 shiny 包中,当在 YAML 元数据指定 runtime: shiny 时,它们会被自动加载。因此,读者不必调用 library(shiny) 来加载 shiny。

5.1.3.1 内部定义应用程序

下面例子将 Shiny 应用程序写在了 Rmarkdown 内部中:

shinyApp(

  ui = fluidPage(
    selectInput("region", "Region:",
                choices = colnames(WorldPhones)),
    plotOutput("phonePlot")
  ),

  server = function(input, output) {
    output$phonePlot = renderPlot({
      barplot(WorldPhones[,input$region]*1000,
              ylab = "Number of Telephones", xlab = "Year")
    })
  },

  options = list(height = 500)
)

注意:使用 height 参数来确定嵌入式应用程序应该占用多少高度。

5.1.3.2 外联应用程序

下面例子展示嵌入了一个定义在另一个目录中的 Shiny 应用程序。

shinyAppDir(
  system.file("examples/06_tabsets", package="shiny"),
  options = list(width = "100%", height = 700)
)

5.1.4 Shiny 小部件

Shiny 小部件让使用者能够使用一个函数调用创建包含在 R Markdown 文档中的可重复使用的 Shiny 组件。还可以直接从控制台调用 Shiny 小部件(在创作过程中很有用),并在 RStudio Viewer 窗格或外部 Web 浏览器中显示它们的输出。

5.1.4.1 shinyApp() 函数

Shiny 窗口小部件的核心是使用 shinyApp() 函数创建的微型应用程序。使用者无需像传统的 Shiny 应用程序那样,既要创建用户界面(UI),又要创建服务器端(Server)。而是将 UI 和 Server 定义作为参数传递给 shinyApp() 函数。 本书会在第 5.1.3.1 节中给出了一个示例。

接下来,先给出最简单的 Shiny 小部件类型———返回 shinyApp() 的 R 函数。

5.1.4.2 例子:k-means 聚类

rmdexamples 包 (https://github.com/rstudio/rmdexamples) 包含以上形式实现的 Shiny 小部件示例。 kmeans_cluster() 函数接受单个数据集(dataset)参数,并返回一个小部件来显示 k-Means 聚类的结果。读者可以在 R Markdown 文档中使用它:

library(rmdexamples)
kmeans_cluster(iris)

5.5 展示了在运行文档时小部件的样子。

 在数据集上应用 k-Means 聚类的 Shiny 小部件。

图 5.5: 在数据集上应用 k-Means 聚类的 Shiny 小部件。

下面是 kmeans_cluster() 函数的源代码:

kmeans_cluster = function(dataset) {

  library(shiny)
  vars = names(dataset)

  shinyApp(
    ui = fluidPage(
      fluidRow(style = "padding-bottom: 20px;",
        column(4, selectInput('xcol', 'X Variable', vars)),
        column(4, selectInput('ycol', 'Y Variable', vars,
                              selected = vars[2])),
        column(4, numericInput('clusters', 'Cluster count', 3,
                               min = 1, max = 9))
      ),
      fluidRow(
        plotOutput('kmeans', height = "400px")
      )
    ),
    server = function(input, output, session) {
      #  将选定的变量组合到一个新的数据框中
      selectedData = reactive({
        dataset[, c(input$xcol, input$ycol)]
      })
      clusters = reactive({
        kmeans(selectedData(), input$clusters)
      })
      output$kmeans = renderPlot(height = 400, {
        res = clusters()
        par(mar = c(5.1, 4.1, 0, 1))
        plot(selectedData(),
             col = res$cluster, pch = 20, cex = 3)
        points(res$centers, pch = 4, cex = 4, lwd = 4)
      })
    },
    options = list(height = 500)
  )
}

5.1.4.3 小部件的大小和布局

Shiny 小部件可以嵌入在不同的地方,包括标准的全宽页面、页面内的小列,甚至在 HTML5 幻灯片中。为了让小部件的大小和布局在上文所述的文档中稳定工作,作者建议小部件的总高度不高于 500 像素。当然,还可以在创建小部件的函数中添加一个显式的 height 参数(默认值为 500)。

5.1.6 Shiny 文档的渲染

5.1.6.1 延迟渲染

Shiny 文档通常在每次 R Markdown 文档渲染时都会显示。这时,较大或计算量较大的文档可能需要一些时间来加载。

如果文档包含不需要立即渲染的交互式 Shiny 组件,可以在 rmarkdown::render_delayed() 函数中封装 Shiny 代码。这个函数保存它的参数,直到文档呈现完成并显示给用户,然后计算它,并在计算完成时将其注入输出文档。

例如,以下代码中,首先渲染出数值输入的结果,加载完文档并显示给用户之后, render_delayed() 才会执行内部代码,并将其加载到文档中。

numericInput("rows", "How many cars?", 5)

rmarkdown::render_delayed({
  renderTable({
    head(cars, input$rows)
  })
})

5.2 交互式仪表盘:Dashboards

本节将介绍通过 flexdashboard(Iannone, Allaire, and Borges 2020) 设计一个仪表盘的方法。

仪表盘在业务风格的报告中特别常见。它们可以用来展示报告的概要和关键内容。仪表盘的布局通常是基于网格搭建的,各个组件排列在各种大小的“盒子”中。

使用 flexdashboard 包,读者可以

  • 通过 R Markdown,展示一组数据的可视化图表。

  • 嵌入各种各样的组件,包括 HTML 小部件、R 图形、表格数据和文本注释等内容。

  • 可以指定按行或列进行布局(各组件会自动调整大小以填满浏览器,并且在移动设备上也十分适配)。

  • 可以创建区域来呈现可视化图形和相关注释。

  • 使用 Shiny 驱动动态可视化图表。

5.2.1 Dashboards 入门

首先,安装 flexdashboard 包:

install.packages("flexdashboard")

其次,通过点击 File -> New File -> R Markdown 对话框在 RStudio 中创建文档,并选择 “Flex Dashboard” 模板。操作界面如图 5.6 所示:

创建新的 dashboard 文件。

图 5.6: 创建新的 dashboard 文件。

:如果没有使用 RStudio 进行操作,那么也可以从 R 控制台创建一个新的 flexdashboard 的 R Markdown 文件,具体操作如下:

rmarkdown::draft(
  "dashboard.Rmd", template = "flex_dashboard",
  package = "flexdashboard"
)

本章只介绍一些基本特性和用法。如果读者想更进一步了解 flexdashboard,可以查看它的完整文档: https://rmarkdown.rstudio.com/flexdashboard/

仪表盘有许多与 HTML 文档相同的特性,比如图形选项,外观和风格,MathJax 公式,头部和正文前后内容和 Pandoc 参数,等等。除此之外,也建议浏览 R 帮助页面 ?flexdashboard::flex_dashboard 来了解更多 flexdashboard 选项和其特性。

当然 RStudio 官网也给出了该包 介绍 与案例,读者可以基于案例到学习实现快速入门。

5.2.2 Dashboards 排版

关于仪表盘布局的总体规则是:

  • 一级标题:生成页面;

  • 二级标题:生成列(或行);

  • 三级标题:生成框(包含一个或多个仪表盘组件)。

下面给出一个简单的例子:

---
title: "入门例子"
output: flexdashboard::flex_dashboard
---

```{r setup, include=FALSE}
library(flexdashboard)
```

第一列
--------------------------------------------------

### 图表 A

```{r}
```

第二列
--------------------------------------------------

### 图表 B

```{r}
```

### 图表 C

```{r}
```

注意,第一行文本(第一列)下的一系列破折号是二级标题的另一种 Markdown 语法形式,即:

第一列
--------------------------------------------------

等同于

## 第一列

这里使用一系列减号,只是为了让二级标题在源文档中更为显眼罢了。读者可以根据自身喜好,选择任意一种语法形式。

默认情况下,二级标题在仪表板上生成列,三级标题在列中自上而下排列。所以在默认情况下,不必在仪表盘上设置列,因为它默认一列一列自上而下排列显示。

注意:二级标题的内容将不会显示在输出中。二级标题仅用于布局(例如,例子中的“第一列” 不会显示在输出中),因此二级标题的实际内容一点都不重要。相比之下,一级标题和三级标题更加重要。

5.7 显示了上述示例的结果,一共是两列,第一列为 “图表 A”,第二列为 “图表 B” 和 “图表 C”。

:本例中,作者并没有在代码块中加入任何 R 代码,因此框内都是空的。在实际使用中,读者可以编写任意的 R 代码来生成 R 图、HTML 小部件或其他组件,并将其加入到框中。

简单仪表盘布局示例。

图 5.7: 简单仪表盘布局示例。

5.2.2.1 基于行的布局

通过修改 orientation 选项将默认布局(列导向)改为以行导向的布局,例如:

output:
  flexdashboard::flex_dashboard:
    orientation: rows

此时,二级结构将会按照行进行排列,三级结构会按照行中的列进行排放。将上述例子修改后,输出结果如图 5.8 所示:

基于行布局的结果。

图 5.8: 基于行布局的结果。

5.2.2.2 节属性

二级结构头部还可以加入一些属性,例如:设置列宽度为 350。

窄栏 {data-width=350}
--------------------------------

在基于行布局的情况下,可以为行设置 data-height 属性。而基于列布局的情况下,可以使用 {.tabset} 使得三级结构以制表符的形式排列,例如:

两个选项卡 {.tabset}
------------------

### 选项卡 A

### 选项卡 B

结果如图 5.9 所示:

以制表符的形式排列。

图 5.9: 以制表符的形式排列。

5.2.2.3 多页

如果 R Markdown 文档中有多个一级结构的内容时,这时仪表盘会将每个一节结构分别显示为单独页面。下面给出一个简单的例子:

---
title: "多页显示"
output: flexdashboard::flex_dashboard
---

图形 {data-icon="fa-signal"}
===================================== 
    
### 图 A
    
```{r}
```
    
### 图 B

```{r}
```
   
表格 {data-icon="fa-table"}
=====================================     

### 表 A
    
```{r}
```
    
### 表 B

```{r}
```
仪表盘上的多个页面情况。

图 5.10: 仪表盘上的多个页面情况。

:多个等号是一级标题的另一种 Markdown 语法(也可以使用单个井号 # 表示)。

从图 5.10 可以看到: 页面标题显示在仪表盘顶部的导航菜单中。一级结构单独构成一个页面。

本例中,还做了一个小拓展,通过 data-icon 属性将图标应用于页面标题中。当然也可以从该网址 https://fontawesome.com 找到其他可用图标。例如,图 5.11 给出部分可用图标。

网站中部分可用图标。

图 5.11: 网站中部分可用图标。

5.2.2.4 故事板

除了基于列或行布局外,还可以通过故事板(storyboard)进行布局,呈现一些可视化图形或其他说明。下面给出一个简单的例子:

---
title: "故事板"
output: 
  flexdashboard::flex_dashboard:
    storyboard: true
---

### 散点图

```{r}
plot(cars, pch = 20)
grid()
```

---

可以添加一些评论。

### 直方图

```{r}
hist(faithful$eruptions, col = 'gray', border = 'white', main = '')
```

---

可以添加一些评论。
基于故事板布局的结果。

图 5.12: 基于故事板布局的结果。

如图 5.12 所示,读者可以通过顶部的左右导航按钮来浏览所有故事板内容。

5.2.3 Dashboards 组件

仪表盘布局中可以包含各种各样的组件,包括:

  1. 基于 HTML 小部件的交互式 JavaScript 数据可视化图形。

  2. R 图形,包括基础、栅栏和网格图形;

  3. 表格(可选选项包括:排序,过滤和分页等);

  4. 数值框(展示重要数据);

  5. 仪表盘;

  6. 文本注释;

  7. 导航栏(提供与仪表盘相关的更多链接)。

:无论输出格式如何,前三个组件在大多数 R Markdown 文档中均可使用。 而后四个组件是仪表盘特有的,本节主要介绍后四个组件。

5.2.3.1 数值框

如果希望在仪表盘中包含一个或多个数值,那么读者可以使用 flexdashboard 包中的 valueBox() 函数来实现这个需求。下面给出一个简单的例子:

---
title: "数值框"
output:
  flexdashboard::flex_dashboard:
    orientation: rows
---

```{r setup, include=FALSE}
library(flexdashboard)
# 这些计算函数只是简单的例子
computeArticles = function(...) return(45)
computeComments = function(...) return(126)
computeSpam = function(...) return(15)
```

### 每日文章数

```{r}
articles = computeArticles()
valueBox(articles, icon = "fa-pencil")
```

### 每日评论数

```{r}
comments = computeComments()
valueBox(comments, icon = "fa-comments")
```

### 每日垃圾邮件数

```{r}
spam = computeSpam()
valueBox(
  spam, icon = "fa-trash",
  color = ifelse(spam > 10, "warning", "primary")
)
```
仪表盘上并排的三个值。

图 5.13: 仪表盘上并排的三个值。

5.13 展示了三个并排的仪表,每个仪表都显示了一个数值和标题。这里重点解释下第三个代码块(### 每日垃圾邮件数)。这里的 valueBox() 函数定义了一个值( spam )和一个图标( icon = "fa-trash" )。并使用 color 设置参数框的颜色。内部使用了一个 ifelse() 语句,使得不同值表示不同的颜色。当然,可用的颜色还包括: "info", "success""danger"(默认值为: "primary")。也可以指定任何有效的 CSS 颜色(例如:"#ffffff""rgb(100, 100, 100)" 等)。

5.2.3.2 仪表

在指定数值范围内显示仪表上的数值。例如,下面展示了三个仪表并排的结果(见图 5.14)。

---
title: "仪表"
output:
  flexdashboard::flex_dashboard:
    orientation: rows
---

```{r setup, include=FALSE}
library(flexdashboard)
```

### 接触率

```{r}
gauge(91, min = 0, max = 100, symbol = '%', gaugeSectors(
  success = c(80, 100), warning = c(40, 79), danger = c(0, 39)
))
```

### 平均额定值

```{r}
gauge(37.4, min = 0, max = 50, gaugeSectors(
  success = c(41, 50), warning = c(21, 40), danger = c(0, 20)
))
```

### 取消数

```{r}
gauge(7, min = 0, max = 10, gaugeSectors(
  success = c(0, 2), warning = c(3, 6), danger = c(7, 10)
))
```
三个仪表并排放在仪表盘上。

图 5.14: 三个仪表并排放在仪表盘上。

这个示例需要解释以下几点:

  1. 通过 gauge() 函数设置一个仪表盘。其内部三个参数需要确定:valueminmax (可以是任何数值)。

  2. 可以指定一个可选的符号(symbol)和值一起显示(本例中, “%” 用来表示百分比)。

  3. 可以使用 gaugeSectors() 函数指定一组自定义的颜色扇区,默认颜色为绿色。扇区选项(sectors)可以指定三个值的范围(success, warningdanger) 使得仪表盘的颜色根据它的值变化而变化。

5.2.3.3 文本注释

可以通过以下方式在仪表盘中包含额外的叙述说明:

  1. 在页面顶部加入相应文本内容。

  2. 定义不包含图表,而是仅包含任意内容(文本、图像和方程等)的指示板。

如图 5.15 所示,顶部包含了一些内容说明和右下角包含了一个只有内容的指示板:

---
title: "文本注释"
output:
  flexdashboard::flex_dashboard:
    orientation: rows
---

英国 1974-1979 年每月死于支气管炎、肺气肿和哮喘的人数 (来源于:P. J. Diggle, 1990, Time Series: A Biostatistical Introduction. Oxford, table A.3)

```{r setup, include=FALSE}
library(dygraphs)
```

行 {data-height=600}
-------------------------------------

### 总肺癌死亡数

```{r}
dygraph(ldeaths)
```

行 {data-height=400}
-------------------------------------

### 男性死亡人数

```{r}
dygraph(mdeaths)
```

> 1974-1979 年英国每月死于肺病的人数。

### 备注

本例使用了 dygraphs 包。该包为绘制时间序列数据提供了丰富的工具。
仪表盘上的文本注释。

图 5.15: 仪表盘上的文本注释。

注意:仪表盘中的每个组件都可以包括标题和注释部分。三级结构 (###) 后面的文本为标题;> 开头的文本是注释。

5.2.3.4 导航栏

默认情况下,仪表盘的导航栏包括:标题(title)、作者(author)和日期(date)。当仪表盘有多个页面时(第 5.2.2.3 节),导航条左侧还包含指向各个页面的链接。当然,也在可以仪表盘上添加社交链接。

除此之外,使用 navbar 选项可以在导航栏中添加自定义链接。例如,在导航栏中添加 “关于” 链接:

---
title: "导航栏"
output:
  flexdashboard::flex_dashboard:
    navbar:
      - { title: "关于", href: "https://example.com/about" }
---

这时得到的界面如图 5.16 所示:

导航栏中添加自定义链接。

图 5.16: 导航栏中添加自定义链接。

注意:导航栏必须包括标题或图标(或两者都包含)。还可以使用 href 作为导航目标。如果想调整文本对齐方式,可以使用 align 参数 (默认情况下为右对齐)。

5.2.4 Dashboards 与 Shiny

在仪表盘中添加 Shiny,可以通过交互界面手动更改参数,并显示实时结果。或者当仪表盘的数据发生变化时,让仪表盘进行实时更新(请参阅 shiny 包中的 reactiveFileReader()reactivePoll() 函数)。这是通过将 runtime: shiny 添加到标准仪表盘文档来实现的,然后添加一个或多个输入控件或响应表达式来动态驱动仪表板内组件的外观。

flexdashboard 中使用 Shiny 可以将一个静态的 R Markdown 报告变成一个交互式文档。需要注意的是,交互式文档需要部署到 Shiny 的服务器上,以便广泛共享(而静态 R Markdown 文档是可以附加到电子邮件或从任何标准 Web 服务器提供的独立 Web 页面)。

注意,shinydashboard 包提供了用 Shiny 创建仪表板的另一种方法。

5.2.4.1 入门指南

在仪表盘中添加 Shiny 组件的步骤如下:

  1. 在文档顶部 YAML 元数据中添加 runtime: shiny

  2. 在仪表盘第一列添加 {.sidebar} 属性,使其成为 Shiny 控件输入的控制台

:这一步不是必须的,但这是基于 Shiny 仪表盘的经典布局。

  1. 根据需求,添加 Shiny 的输入和输出。

  2. 当代码中包含绘图函数时(例如:hist()),得将它们封装在 renderPlot() 中。这有利于界面在布局更改时,自动调整尺寸大小。

5.2.4.2 Shiny 仪表盘的一个示例

5.17 给出了 Shiny 仪表盘的一个示例:

---
title: "间歇泉喷发"
output: flexdashboard::flex_dashboard
runtime: shiny
---

```{r global, include=FALSE}
# 在全局块中加载数据,以便仪表盘的所有用户可以共享
library(datasets)
data(faithful)
```

列 {.sidebar}
--------------------------------------------------

美国怀俄明州黄石国家公园的 Old Faithful  间歇泉在喷发和喷发之间的等待时间。

```{r}
selectInput(
  "n_breaks", label = "条形数:",
  choices = c(10, 20, 35, 50), selected = 20
)

sliderInput(
  "bw_adjust", label = "带宽调整:",
  min = 0.2, max = 2, value = 1, step = 0.2
)
```


--------------------------------------------------

### 间歇泉喷发持续时间

```{r}
renderPlot({
  erpt = faithful$eruptions
  hist(
    erpt, probability = TRUE, breaks = as.integer(input$n_breaks),
    xlab = "Duration (minutes)", main = "Geyser Eruption Duration",
    col = 'gray', border = 'white'
  )
  
  dens = density(erpt, adjust = input$bw_adjust)
  lines(dens, col = "blue", lwd = 2)
})
```
基于 Shiny 的交互式仪表盘。

图 5.17: 基于 Shiny 的交互式仪表盘。

其中,仪表盘的第一列包含了 {.sidebar} 属性和两个 Shiny 的输入控件;第二列包含了绘制图表的 Shiny 代码。

注意:文档顶部标记为 global 的 R 代码块在全局环境中都可以被调用。这将为用户带来更好的启动性能,强烈推荐大家使用。

5.2.4.3 输入栏

通过添加 {.sidebar} 属性设置一个默认布局为左对齐,250 像素宽度的左侧边栏。

在搭建多个页面的仪表盘时,如果想创建一个应用于所有页面的工具条。这时,可以使用一级结构来定义侧边栏。

5.2.4.4 拓展

下面给出一些学习 Shiny 和创建交互式文档的资源:

  1. Shiny 官方网站( http://shiny.rstudio.com) :包含大量的文章、教程和示例。

  2. Shiny 网站上的文章“Introduction to Interactive Documents”,这是一个很好的入门指南。

  3. 关于部署交互式文档,读者可以使用 Shiny Server 或 RStudio Connect:https://www.rstudio.com/products/shiny/shiny-server/

5.3 在 R package 中使用 R Markdown

5.3.1 写软件包的文档

如果有开发 R 包的经验,或者项目中编写的自定义函数需要清晰的文档和严格的测试,那么可以考虑将项目制作成一个 R 包。如果不知道如何创建一个 R 包,则可以很容易地在 RStudio IDE 中通过点击菜单 File -> New Project 开始,选择项目类型为 R Package。此外,第 4.2 节还介绍了如何使用 R Markdown 开发 R 包,可以大大加速 R 包开发过程。

使用 R 包来管理项目有很多好处。例如,可以将数据集放在 data/ 文件夹中,在 R/ 下写 R 代码,生成文档到 man/(例如,使用 roxygen2(R-roxygen2?)),并将单元测试添加到 test/ 中。当涉及到 R Markdown 报告时,可以将它们作为 vignettes/ 下的软件包长文档来编写。在这些长文档中,可以加载数据集并调用包中的函数。当构建包时(通过 R CMD build 或 RStudio 命令),长文档将被自动编译。

要在 R Markdown 中创建一个包的长文档,最简单的方法是通过 RStudio 菜单 File -> New File -> R Markdown -> From Template(见图5.18)。然后从 rmarkdown 包中选择 Package Vignette,将得到一个长文档的模板。在更改模板的标题、作者和其他元数据之后,就可以开始编写报告的内容了。

在 RStudio 中创建一个包的长文档。

图 5.18: 在 RStudio 中创建一个包的长文档。

或者,也可以安装包 usethis (R-usethis?) 并使用其中的函数 usethis::use_vignette() 来创建一个长文档框架。下面是软件包长文档的 YAML 前端内容通常情况下的样子:

---
title: "Vignette Title"
author: "Vignette Author"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Vignette Title}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

需要注意的是,需要在 title 字段和 \VignetteIndexEntry{} 命令中更改长文档的标题。除了长文档中的上述信息外,还需要在包的 DESCRIPTION 文件中做另外两件事:

  1. DESCRIPTION 文件中指定 VignetteBuilder: knitr

  2. DESCRIPTION 文件中添加 Suggests: knitr, rmarkdown

长文档的输出格式不一定非得是 HTML,也可以是 PDF,所以也可以使用 output: pdf_document。创建除 HTML 和 PDF 之外的任何其他输出格式也可以,比如 beamer_presentationtufte::tufte_html。然而,目前 R 只识别 HTML 和 PDF 格式的长文档。

5.3.2 R 包中的 R Markdown 模板

5.3.1 节的图 5.18 给出了从 rmarkdown 包中检索可编辑的 Package Vignette(HTML)模板的过程。这个 R Markdown 文件预先填充了适用于 R 包长文档的元数据。

类似地,任何包都可以包含 R Markdown 模板,包的用户可以通过 RStudio IDE(如图 5.18 所示)或使用 rmarkdown::draft() 函数访问这些模板。

5.3.2.1 模板使用样例

模板是共享自定义结构、样式和内容的有效的方法,很多 R 包都使用了模版文档都方式提高代码的复用率。

许多模板通过预填充 YAML 元数据来添加结构和样式,例如 rmarkdown 包的 Package Vignette (HTML)模板。类似地,rmdformats(R-rmdformats?)提供了许多模板,这些模板将不同的自定义的样式函数传递给 output 选项。

其他模板演示了包所需的文档结构。例如,pagedown(R-pagedown?)包含了许多海报、简历和其他页面布局的模板。类似地,xaringan 包的 Ninja Presentation 模板(Xie 2022e)展示了许多不同幻灯片格式选项的语法。

模板也可以展示包的特性和语法。例如,flexdashboard(Iannone, Allaire, and Borges 2020)learnr(R-learnr?)都包含了带有代码块的模板,这些代码块分别调用包中的函数来创建示例仪表板或教程。

类似地,模板也可以包含样板内容。例如,rticles(Allaire, Xie, Dervieux, et al. 2022)提供了许多这样的模板,以使 R Markdown 输出符合不同学术期刊所需的风格和内容指导。样板内容在组织设置中也很有用,比如生成季度报告的团队。

5.3.2.2 模版设置

usethis(R-usethis?)可以创建 R Markdown 模版。运行 usethis::use_rmarkdown_template("Template Name") 将自动创建所需的目录结构和文件(模板名应自己提供)。

如果想手动设置模板,可以在 inst/rmarkdown/templates 目录下创建一个子目录。在这个目录中,需要保存至少两个文件:

  1. 一个名为 template.yaml 的文件,它为 RStudio IDE 提供了基本的元数据,比如模板的可读名称。这个文件至少应该有 namedescription 字段,例如:

    name: 模板样例
    description: 这个模板做了什么

    如果想在模板被选用时创建一个新目录,可以包含 create_dir: true,如果模板依赖于额外的资源,这也是有用的。例如,learnr 包的模板 设置了 create_dir: true,而 flexdashboard 包的模板则使用默认的 create_dir: false。可以尝试在 RStudio 中打开这两个模板,以注意到不同的用户提示。

  2. 保存在 skeleton/skeleton.Rmd 下 R Markdown 文档。这可能包含希望放入 R Markdown 文档中的任何内容。

可供选择的是,skeleton 文件夹可能还包括其他资源,如模板使用的样式表或图像。这些文件将与模板一起被加载到用户的计算机中。

建立自定义 R Markdown 模板的更多细节,请参阅 RStudio 扩展 的网站,以及 R Markdown Definitive Guide 的第十七章 “Document Templates”(Xie, Allaire, and Grolemund 2018a)

参考文献

Allaire, JJ, Yihui Xie, Christophe Dervieux, R Foundation, Hadley Wickham, Journal of Statistical Software, Ramnath Vaidyanathan, et al. 2022. Rticles: Article Formats for r Markdown. https://github.com/rstudio/rticles.
Iannone, Richard, JJ Allaire, and Barbara Borges. 2020. Flexdashboard: R Markdown Format for Flexible Dashboards. http://rmarkdown.rstudio.com/flexdashboard.
———. 2022e. Xaringan: Presentation Ninja. https://github.com/yihui/xaringan.
Xie, Yihui, J. J. Allaire, and Garrett Grolemund. 2018a. R Markdown: The Definitive Guide. Boca Raton, Florida: Chapman; Hall/CRC. https://bookdown.org/yihui/rmarkdown.