-
Notifications
You must be signed in to change notification settings - Fork 28
/
05-interaction.Rmd
711 lines (434 loc) · 35.3 KB
/
05-interaction.Rmd
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
# (PART) R Markdown 进阶操作 {.unnumbered}
# 使用 R Markdown 创建动态交互文档 {#rmarkdown-interaction}
## Web 交互界面:Shiny 小程序 {#rmarkdown-shiny}
Shiny 小程序是由 R 驱动的交互式 Web 应用程序。基于 Shiny 的 Web 应用程序(Shiny App)用途十分广泛,功能也很强大,实现的方式也多种多样。本书主要介绍与 R Markdown 文档应用相关的一部分知识,欲了解更多请参见 [Shiny 官网](https://shiny.rstudio.com/)。
Shiny 包是由 R 驱动的交互式 Web 应用程序。如果读者想从 R Markdown 文档中调用 Shiny 代码,只需在 YAML 元数据中添加 `runtime: shiny` 即可。由于 Shiny 构建 Web 应用程序功能十分强大,本书只介绍部分的 Shiny 知识。在阅读本章之前,如果不了解该包,可以先通过以下网站 https://shiny.rstudio.com 进行大致了解。
### 入门指南 {#shiny-start}
在 YAML 元数据中加入 `runtime: shiny`,可以将任何基于 html 的 R Markdown 文档变成 Shiny 文档,例如:
```yaml
---
title: "Shiny Document"
output: html_document
runtime: shiny
---
```
注意,R Markdown 文档的输出格式必须是 HTML 格式。也就是说,文档最后生成的是一个 Web 页面(`*.html` 文件)。
> **注意:** 非 `html` 格式,如 `pdf_document` 和 `word_document` 将不能与 Shiny 同时运行。另外,一些演示格式也是 HTML 格式,比如`ioslides_presentation` 和 `slidy_presentation`,它们是可以与 Shiny 相结合的。
当然,也可以通过 RStudio 构建一个新的 Shiny 文档。步骤如下:File -> new File -> R Markdown 并选择 Shiny,具体见图 \@ref(fig:shiny-new-document)。
```{r shiny-new-document, echo=FALSE, fig.width=6, fig.align='center', fig.cap=' 在 RStudio 中创建一个新的 Shiny 文档。', out.width = "90%"}
knitr::include_graphics('images/shiny-new-document.png', dpi = NA)
```
如果读者想在 RStudio 中运行 Shiny 文档,需要单击工具栏上的 "Run Document" 按钮(当 RStudio 检测到这是 Shiny 文档时,它会自动将 Knit 按钮替换为 Run Document)。如果读者没有使用 RStudio,或者想在 R 控制台运行文档进行故障排除,可以调用函数 `rmarkdown::run()` 并将文件名传递给它。
可以在文档中嵌入 Shiny 的输入和输出。如果输入发生变化时,输出将自动更新。例如,创建一个名称为 `rows` 的数字输入( `numericInput` ),然后在输出中通过 `input$rows` 引用其值:
```{r, eval = FALSE}
numericInput("rows", "How many cars?", 5)
renderTable({
head(cars, input$rows)
})
```
```{r shiny-table, echo=FALSE, fig.width=6,fig.align='center',fig.cap='增加 Shiny 文档中表中的行数', out.width = "90%"}
knitr::include_graphics('images/shiny-table.png', dpi = NA)
```
在上面的示例中,输出代码包含在 `renderTable()` 中。 Shiny 中还有许多其他渲染功能,可用于图片、结果输出等。 下面使用 `renderPlot()` 输出可交互柱状图:
````{verbatim}
```{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')
})
```
````
```{r shiny-plot, echo=FALSE, fig.cap ='Shiny 文档输出可交互的直方图', out.width = "90%"}
knitr::include_graphics('images/shiny-plot.png', dpi = NA)
```
### Shiny 部署 {#shiny-deploy}
如果在本地运行一个 Shiny 文档时,默认情况下它会使用本地 R 会话,并且只有运行的人能与该文档进行交互。如果希望与没有安装 R 的用户共享文档,或者不希望在本地运行文档,则必须将文档部署到服务器上,并共享文档的 URL。那么其他人只需要一个 web 浏览器就可以访问文档。
有两种方法可以部署 Shiny 文档。1. 使用 RStudio 提供的托管服务;2. 搭载到自己的服务器上。
<!-- 第一种方法从技术上来说比较容易,但是有时可能不被允许使用外部托管服务,因此读者必须在自己的服务器上安装必需的软件(Shiny Server 或 RStudio Connect)\index{Shiny Server}\index{RStudio Connect}才能部署 Shiny 文档。 -->
#### ShinyApps.io
读者可以把 Shiny 文档发布到 ShinyApps (https://shinyapps.io) 进行托管。
需要满足以下两个条件:
1. 在 ShinyApps 上注册账号。
2. 安装了最新版本的 **rsconnect** 包。安装方式如下:
```r
install.packages("rsconnect")
```
然后,对需要部署的 Shiny 文档所对应的工作目录中执行以下语句:
```r
rsconnect::deployApp()
```
RStudio 用户也可以在运行 Shiny 应用后,点击窗口右上角的 `Publish` 按钮(图 \@ref(fig:shiny-deploy))。
```{r shiny-deploy, echo=FALSE, fig.cap='部署 Shiny 文档到 ShinyApps.io. 上', out.width = "90%"}
knitr::include_graphics('images/shiny-deploy.png', dpi = NA)
```
如果目录中有一个名为 `index.Rmd` 的文件,它将用作该目录的默认文档。如果想访问 Rmd 文档,则应在 URL 中指定 Rmd 文件的显式路径。
例如,部署到 ShinyApps 的 `index.Rmd` 的 URL 可以采用以下形式 `https://example.shinyapps.io/appName/`,而 `test.Rmd` 的 URL 可以采用以下形式 `https://example.shinyapps.io/appName/test.Rmd`。
#### Shiny Server / RStudio Connect
除此之外,Shiny Server (https://www.rstudio.com/products/shiny/shiny-server/) 和 RStudio Connect(https://www.rstudio.com/products/connect/) 都可以发布 Shiny 文档,但是读者需要熟悉有关 Linux 的知识。由于该内容较为复杂与丰富,超出本书读者的需求,所以在此不做过多介绍。
### 嵌入 Shiny 应用程序 {#shiny-app}
除了在 R Markdown 中嵌入单个 Shiny 的输入和输出外,还可以在文档中嵌入一个独立的 Shiny 应用程序。有两种方法可以做到这一点:
1. 使用 `shinyApp()` 函数内部定义应用程序;
2. 使用 `shinyAppDir()` 函数引用外部应用程序。
> **注意**:这两个函数在 **shiny** 包中,当在 YAML 元数据指定 `runtime: shiny` 时,它们会被自动加载。因此,读者不必调用 `library(shiny)` 来加载 shiny。
#### 内部定义应用程序 {#shiny-embed-inline}
下面例子将 Shiny 应用程序写在了 Rmarkdown 内部中:
```{r, eval = FALSE}
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` 参数来确定嵌入式应用程序应该占用多少高度。
#### 外联应用程序
下面例子展示嵌入了一个定义在另一个目录中的 Shiny 应用程序。
```{r, eval = FALSE}
shinyAppDir(
system.file("examples/06_tabsets", package="shiny"),
options = list(width = "100%", height = 700)
)
```
### Shiny 小部件 {#shiny-widget}
Shiny 小部件\index{Shiny widget}让使用者能够使用一个函数调用创建包含在 R Markdown 文档中的可重复使用的 Shiny 组件。还可以直接从控制台调用 Shiny 小部件(在创作过程中很有用),并在 RStudio Viewer 窗格或外部 Web 浏览器中显示它们的输出。
#### `shinyApp()` 函数
Shiny 窗口小部件的核心是使用 `shinyApp()` 函数创建的微型应用程序。使用者无需像传统的 Shiny 应用程序那样,既要创建用户界面(UI),又要创建服务器端(Server)。而是将 UI 和 Server 定义作为参数传递给 `shinyApp()` 函数。 本书会在第 \@ref(shiny-embed-inline) 节中给出了一个示例。
接下来,先给出最简单的 Shiny 小部件类型———返回 `shinyApp()` 的 R 函数。
#### 例子:k-means 聚类
**rmdexamples** 包 (https://github.com/rstudio/rmdexamples) 包含以上形式实现的 Shiny 小部件示例。 `kmeans_cluster()` 函数接受单个数据集(`dataset`)参数,并返回一个小部件来显示 k-Means 聚类的结果。读者可以在 R Markdown 文档中使用它:
```{r, eval = FALSE}
library(rmdexamples)
kmeans_cluster(iris)
```
图 \@ref(fig:shiny-widget-kmeans) 展示了在运行文档时小部件的样子。
```{r shiny-widget-kmeans, echo=FALSE, fig.cap=' 在数据集上应用 k-Means 聚类的 Shiny 小部件。', out.width = "90%"}
knitr::include_graphics('images/shiny-widget-kmeans.png', dpi = NA)
```
下面是 `kmeans_cluster()` 函数的源代码:
```r
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)
)
}
```
#### 小部件的大小和布局
Shiny 小部件可以嵌入在不同的地方,包括标准的全宽页面、页面内的小列,甚至在 HTML5 幻灯片中。为了让小部件的大小和布局在上文所述的文档中稳定工作,作者建议小部件的总高度不高于 500 像素。当然,还可以在创建小部件的函数中添加一个显式的 height 参数(默认值为 500)。
### Shiny 文档间的链接 {#shiny-link}
可以使用 Markdown 链接语法并指定文档的**相对**路径,链接到其他 Shiny 文档上,例如: `[另一个Shiny文档](another.Rmd)`。如果在一个页面上单击指向另一个 Rmd 文档的链接,该 Rmd 文档将作为当前交互式 Shiny 文档启动。
默认情况下,只能链接到调用 `rmarkdown::run()` 的文件所在目录中的 R Markdown 文件(例如,无法连接到 `../foo.Rmd`)。 但是,可以使用 `rmarkdown::run()` 的 `dir` 参数来定义根目录。
### Shiny 文档的渲染 {#shiny-render}
#### 延迟渲染 {#shiny-delay}
Shiny 文档通常在每次 R Markdown 文档渲染时都会显示。这时,较大或计算量较大的文档可能需要一些时间来加载。
如果文档包含不需要立即渲染的交互式 Shiny 组件,可以在 `rmarkdown::render_delayed()` 函数中封装 Shiny 代码。这个函数保存它的参数,直到文档呈现完成并显示给用户,然后计算它,并在计算完成时将其注入输出文档。
例如,以下代码中,首先渲染出数值输入的结果,加载完文档并显示给用户之后, `render_delayed()` 才会执行内部代码,并将其加载到文档中。
```{r, eval = FALSE}
numericInput("rows", "How many cars?", 5)
rmarkdown::render_delayed({
renderTable({
head(cars, input$rows)
})
})
```
<!-- #### 渲染函数的输出参数 {#shiny-args} -->
<!-- 在一个经典的 Shiny 应用程序中,读者可以使用 `plotOutput()` 和 `verbatimTextOutput()` 等函数在 UI 中指定输出元素,并使用 `renderPlot()` 和 `renderPrint()` 等函数呈现其内容。 -->
<!-- 相比之下,在 Shiny 文档中,UI 元素通常是在调用 `renderXXX()` 函数时自动创建的。例如,使用 `renderPlot()` 函数,而预先没有创建对应的 `plotOutput()`。在这种情况下,Shiny 将相应的输出对象与每个 `renderXXX()` 函数关联起来,使得可以在一个完整的 Shiny 应用程序之外使用 Shiny 代码。但是,在这个过程中可能会丢失一些功能。尤其是,`plotOutput()` 可以接受一些可选参数来设置宽度和高度等内容。 -->
<!-- 要将选项从 `renderXXX()` 传递到 `xxxOutput()`,可以使用 `outputArgs` 参数。 例如:要绘制一张宽度为 200px,高度为 100px 的图形时。 可以设定为如下形式: -->
<!-- ````{verbatim} -->
<!-- ```{r, echo = FALSE} -->
<!-- renderPlot({ -->
<!-- plot(yourData) -->
<!-- }, outputArgs = list(width = "200px", height = "100px") -->
<!-- ) -->
<!-- ``` -->
<!-- ```` -->
<!-- 无论想设置多少个输出参数,`outputArgs` 总是设定为一个列表(默认是一个空列表,它不设置输出参数)。如果试图传入一个不存在的参数,那么将得到以下的错误提示信息(例如:在本例中,试图设置一个名为 `not_an_argument` 的参数): -->
<!-- ```markdown -->
<!-- **Error**: Unused argument: in `outputArgs`, `not_an_argument` -->
<!-- is not an valid argument for the output function -->
<!-- ``` -->
<!-- 读者如果想查看运行中的 `outputArgs`,请运行下面的 R Markdown 文档或访问在线版 <https://gallery.shinyapps.io/output-args/>。 -->
<!-- ````{verbatim} -->
<!-- `r xfun::file_string('examples/shiny-output-args.Rmd')` -->
<!-- ```` -->
<!-- ##### 附加说明{#shiny-extra} -->
<!-- 需要强调的是,读者只能在一个 Shiny 的 R Markdown 文档中使用这个功能。也就是说,必须在 YAML 元数据中设置 `runtime: shiny` 。但即使是这样,这也只适用于在 UI 中没有相应的显式输出元素的情况下呈现输出的 Shiny 代码片段。如果读者想在文档中嵌入一个完整的 Shiny 的应用程序,并尝试使用 `outputArgs` ,这时它将被忽略,并将以下警告输出到 R Markdown 控制台中 (在这种情况下, `ui` 函数将类似于 `ui = plotOutput("plot")` ): -->
<!-- ```markdown -->
<!-- Warning in `output$plot`(...) : -->
<!-- Unused argument: outputArgs. The argument outputArgs is only -->
<!-- meant to be used when embedding snippets of Shiny code in an -->
<!-- R Markdown code chunk (using runtime: shiny). When running a -->
<!-- full Shiny app, please set the output arguments directly in -->
<!-- the corresponding output function of your UI code. -->
<!-- ``` -->
<!-- 如果尝试在上文所述的任意文档中使用 `outputArgs`,例如:在常用的(即非嵌入式)Shiny 应用程序内部使用 `outputArgs`,也会发生同样的情况。 -->
## 交互式仪表盘:Dashboards {#rmarkdown-dashboards}
本节将介绍通过 **flexdashboard** 包 [@R-flexdashboard] 设计一个仪表盘\index{dashboard}的方法。
仪表盘在业务风格的报告中特别常见。它们可以用来展示报告的概要和关键内容。仪表盘的布局通常是基于网格搭建的,各个组件排列在各种大小的“盒子”中。
使用 **flexdashboard** 包,读者可以
- 通过 R Markdown,展示一组数据的可视化图表。
- 嵌入各种各样的组件,包括 HTML 小部件、R 图形、表格数据和文本注释等内容。
- 可以指定按行或列进行布局(各组件会自动调整大小以填满浏览器,并且在移动设备上也十分适配)。
- 可以创建区域来呈现可视化图形和相关注释。
- 使用 Shiny 驱动动态可视化图表。
### Dashboards 入门 {#dashboards-intro}
首先,安装 `flexdashboard` 包:
```
install.packages("flexdashboard")
```
其次,通过点击 `File -> New File -> R Markdown` 对话框在 RStudio 中创建文档,并选择 "Flex Dashboard" 模板。操作界面如图 \@ref(fig:dashboard-new) 所示:
```{r dashboard-new, echo=FALSE,, fig.cap='创建新的 dashboard 文件。', out.width = "90%"}
knitr::include_graphics('images/dashboard-new.png', dpi = NA)
```
> **注**:如果没有使用 RStudio 进行操作,那么也可以从 R 控制台创建一个新的 `flexdashboard` 的 R Markdown 文件,具体操作如下:
```r
rmarkdown::draft(
"dashboard.Rmd", template = "flex_dashboard",
package = "flexdashboard"
)
```
本章只介绍一些基本特性和用法。如果读者想更进一步了解 **flexdashboard**,可以查看它的完整文档: https://rmarkdown.rstudio.com/flexdashboard/ 。
仪表盘有许多与 HTML 文档相同的特性,比如图形选项,外观和风格,MathJax 公式,头部和正文前后内容和 Pandoc 参数,等等。除此之外,也建议浏览 R 帮助页面 `?flexdashboard::flex_dashboard` 来了解更多 `flexdashboard` 选项和其特性。
当然 RStudio 官网也给出了该包 [介绍](https://pkgs.rstudio.com/flexdashboard/) 与案例,读者可以基于案例到学习实现快速入门。
### Dashboards 排版 {#dashboards-layout}
关于仪表盘布局的总体规则是:
- 一级标题:生成页面;
- 二级标题:生成列(或行);
- 三级标题:生成框(包含一个或多个仪表盘组件)。
下面给出一个简单的例子:
`r import_example('examples/dashboard/01-start.Rmd')`
注意,第一行文本(第一列)下的一系列破折号是二级标题的另一种 Markdown 语法形式,即:
```markdown
第一列
--------------------------------------------------
```
等同于
```markdown
## 第一列
```
> 这里使用一系列减号,只是为了让二级标题在源文档中更为显眼罢了。读者可以根据自身喜好,选择任意一种语法形式。
默认情况下,二级标题在仪表板上生成列,三级标题在列中自上而下排列。所以在默认情况下,不必在仪表盘上设置列,因为它默认一列一列自上而下排列显示。
> **注意**:二级标题的内容将不会显示在输出中。二级标题仅用于布局(例如,例子中的“第一列” 不会显示在输出中),因此二级标题的实际内容一点都不重要。相比之下,一级标题和三级标题更加重要。
图 \@ref(fig:dashboard-start) 显示了上述示例的结果,一共是两列,第一列为 “图表 A”,第二列为 “图表 B” 和 “图表 C”。
> **注**:本例中,作者并没有在代码块中加入任何 R 代码,因此框内都是空的。在实际使用中,读者可以编写任意的 R 代码来生成 R 图、HTML 小部件或其他组件,并将其加入到框中。
```{r dashboard-start, echo=FALSE, fig.cap='简单仪表盘布局示例。', out.width = "90%"}
knitr::include_graphics('images/dashboard-start.png', dpi = NA)
```
#### 基于行的布局
通过修改 `orientation` 选项将默认布局(列导向)改为以行导向的布局,例如:
```yaml
output:
flexdashboard::flex_dashboard:
orientation: rows
```
此时,二级结构将会按照行进行排列,三级结构会按照行中的列进行排放。将上述例子修改后,输出结果如图 \@ref(fig:dashboard-rows) 所示:
```{r dashboard-rows, echo=FALSE, fig.cap='基于行布局的结果。', out.width = "90%"}
knitr::include_graphics('images/dashboard-rows.png', dpi = NA)
```
#### 节属性
二级结构头部还可以加入一些属性,例如:设置列宽度为 350。
```markdown
窄栏 {data-width=350}
--------------------------------
```
在基于行布局的情况下,可以为行设置 `data-height` 属性。而基于列布局的情况下,可以使用 `{.tabset}` 使得三级结构以制表符的形式排列,例如:
```markdown
两个选项卡 {.tabset}
------------------
### 选项卡 A
### 选项卡 B
```
结果如图 \@ref(fig:dashboard-tab) 所示:
```{r dashboard-tab, echo=FALSE, fig.cap='以制表符的形式排列。', out.width='90%'}
knitr::include_graphics('images/dashboard-tab.png', dpi = NA)
```
#### 多页 {#multiple-pages}
如果 R Markdown 文档中有多个一级结构的内容时,这时仪表盘会将每个一节结构分别显示为单独页面。下面给出一个简单的例子:
```{r dashboard-pages-code,echo=FALSE}
import_example('examples/dashboard/02-pages.Rmd')
```
```{r dashboard-pages, echo=FALSE, fig.cap='仪表盘上的多个页面情况。', out.width = "90%"}
knitr::include_graphics('images/dashboard-multipages.png', dpi = NA)
```
> **注**:多个等号是一级标题的另一种 Markdown 语法(也可以使用单个井号 `#` 表示)。
从图 \@ref(fig:dashboard-pages) 可以看到:
页面标题显示在仪表盘顶部的导航菜单中。一级结构单独构成一个页面。
本例中,还做了一个小拓展,通过 `data-icon` 属性将图标应用于页面标题中。当然也可以从该网址 https://fontawesome.com 找到其他可用图标。例如,图 \@ref(fig:dashboard-icon) 给出部分可用图标。
```{r dashboard-icon, echo=FALSE,fig.cap='网站中部分可用图标。', out.width = "90%"}
knitr::include_graphics('images/dashboard-icon.png', dpi = NA)
```
#### 故事板
除了基于列或行布局外,还可以通过故事板(storyboard)进行布局,呈现一些可视化图形或其他说明。下面给出一个简单的例子:
`r import_example('examples/dashboard/03-storyboard.Rmd')`
```{r dashboard-story, echo=FALSE, fig.cap='故事板布局。', out.width = "90%"}
knitr::include_graphics('images/dashboard-story.png', dpi = NA)
```
如图 \@ref(fig:dashboard-story) 所示,读者可以通过顶部的左右导航按钮来浏览所有故事板内容。
### Dashboards 组件 {#dashboards-module}
仪表盘布局中可以包含各种各样的组件,包括:
1. 基于 HTML 小部件的交互式 JavaScript 数据可视化图形。
1. R 图形,包括基础、栅栏和网格图形;
1. 表格(可选选项包括:排序,过滤和分页等);
1. 数值框(展示重要数据);
1. 仪表盘;
1. 文本注释;
1. 导航栏(提供与仪表盘相关的更多链接)。
> **注**:无论输出格式如何,前三个组件在大多数 R Markdown 文档中均可使用。 而后四个组件是仪表盘特有的,本节主要介绍后四个组件。
#### 数值框
如果希望在仪表盘中包含一个或多个数值,那么读者可以使用 **flexdashboard** 包中的 `valueBox()` 函数来实现这个需求。下面给出一个简单的例子:
`r import_example('examples/dashboard/04-valueboxes.Rmd')`
```{r dashboard-valueboxes, echo=FALSE, fig.cap='仪表盘上并排的三个值。', out.width = "90%"}
knitr::include_graphics('images/dashboard-valueboxes.png', dpi = NA)
```
图 \@ref(fig:dashboard-valueboxes) 展示了三个并排的仪表,每个仪表都显示了一个数值和标题。这里重点解释下第三个代码块(`### 每日垃圾邮件数`)。这里的 `valueBox()` 函数定义了一个值( `spam` )和一个图标( `icon = "fa-trash"` )。并使用 `color` 设置参数框的颜色。内部使用了一个 `ifelse()` 语句,使得不同值表示不同的颜色。当然,可用的颜色还包括: `"info"`, `"success"` 和 `"danger"`(默认值为: `"primary"`)。也可以指定任何有效的 CSS 颜色(例如:`"#ffffff"`, `"rgb(100, 100, 100)"` 等)。
#### 仪表
在指定数值范围内显示仪表上的数值。例如,下面展示了三个仪表并排的结果(见图 \@ref(fig:dashboard-gauges))。
`r import_example('examples/dashboard/05-gauges.Rmd')`
```{r dashboard-gauges, echo=FALSE, fig.cap='并排仪表', out.width = "90%"}
knitr::include_graphics('images/dashboard-gauges.png', dpi = NA)
```
这个示例需要解释以下几点:
1. 通过 `gauge()` 函数设置一个仪表盘。其内部三个参数需要确定:`value`, `min` 和 `max` (可以是任何数值)。
1. 可以指定一个可选的符号(`symbol`)和值一起显示(本例中, "`%`" 用来表示百分比)。
1. 可以使用 `gaugeSectors()` 函数指定一组自定义的颜色扇区,默认颜色为绿色。扇区选项(`sectors`)可以指定三个值的范围(`success`, `warning` 和 `danger`) 使得仪表盘的颜色根据它的值变化而变化。
#### 文本注释
可以通过以下方式在仪表盘中包含额外的叙述说明:
1. 在页面顶部加入相应文本内容。
1. 定义不包含图表,而是仅包含任意内容(文本、图像和方程等)的指示板。
如图 \@ref(fig:dashboard-text) 所示,顶部包含了一些内容说明和右下角包含了一个只有内容的指示板:
`r import_example('examples/dashboard/06-text.Rmd')`
```{r dashboard-text, echo=FALSE, fig.cap='仪表盘上的文本注释。', out.width = "90%"}
knitr::include_graphics('images/dashboard-text.png', dpi = NA)
```
> **注意**:仪表盘中的每个组件都可以包括标题和注释部分。三级结构 (`###`) 后面的文本为标题;`>` 开头的文本是注释。
<!-- 通过将 `.no-title` 属性应用于一个节标题,可以完全排除该标题。 -->
#### 导航栏
默认情况下,仪表盘的导航栏包括:标题(`title`)、作者(`author`)和日期(`date`)。当仪表盘有多个页面时(第 \@ref(multiple-pages) 节),导航条左侧还包含指向各个页面的链接。当然,也在可以仪表盘上添加社交链接。
除此之外,使用 `navbar` 选项可以在导航栏中添加自定义链接。例如,在导航栏中添加 “关于” 链接:
```yaml
---
title: "导航栏"
output:
flexdashboard::flex_dashboard:
navbar:
- { title: "关于", href: "https://example.com/about" }
---
```
这时得到的界面如图 \@ref(fig:dashboard-about) 所示:
```{r dashboard-about, echo=FALSE, fig.cap='导航栏中添加自定义链接。', out.width = "90%"}
knitr::include_graphics('images/dashboard-about.png', dpi = NA)
```
> **注意**:导航栏必须包括标题或图标(或两者都包含)。还可以使用 `href` 作为导航目标。如果想调整文本对齐方式,可以使用 `align` 参数 (默认情况下为右对齐)。
### Dashboards 与 Shiny {#dashboards-shiny}
在仪表盘中添加 Shiny\index{Shiny},可以通过交互界面手动更改参数,并显示实时结果。或者当仪表盘的数据发生变化时,让仪表盘进行实时更新(请参阅 **shiny** 包中的 `reactiveFileReader()` 和 `reactivePoll()` 函数)。这是通过将 `runtime: shiny` 添加到标准仪表盘文档来实现的,然后添加一个或多个输入控件或响应表达式来动态驱动仪表板内组件的外观。
在 **flexdashboard** 中使用 Shiny 可以将一个静态的 R Markdown 报告变成一个交互式文档。需要注意的是,交互式文档需要部署到 Shiny 的服务器上,以便广泛共享(而静态 R Markdown 文档是可以附加到电子邮件或从任何标准 Web 服务器提供的独立 Web 页面)。
注意,[**shinydashboard**](https://rstudio.github.io/shinydashboard/) 包提供了用 Shiny 创建仪表板的另一种方法。
#### 入门指南
在仪表盘中添加 Shiny 组件的步骤如下:
1. 在文档顶部 YAML 元数据中添加 `runtime: shiny`。
1. 在仪表盘第一列添加 `{.sidebar}` 属性,使其成为 Shiny 控件输入的控制台
> **注**:这一步不是必须的,但这是基于 Shiny 仪表盘的经典布局。
1. 根据需求,添加 Shiny 的输入和输出。
1. 当代码中包含绘图函数时(例如:`hist()`),得将它们封装在 `renderPlot()` 中。这有利于界面在布局更改时,自动调整尺寸大小。
#### Shiny 仪表盘的一个示例
图 \@ref(fig:dashboard-shiny) 给出了 Shiny 仪表盘的一个示例:
`r import_example('examples/dashboard/07-shiny.Rmd')`
```{r dashboard-shiny, echo=FALSE, fig.cap='基于 Shiny 的交互式仪表盘。', out.width = "90%"}
knitr::include_graphics('images/dashboard-shiny.png', dpi = NA)
```
其中,仪表盘的第一列包含了 `{.sidebar}` 属性和两个 Shiny 的输入控件;第二列包含了绘制图表的 Shiny 代码。
> **注意**:文档顶部标记为 `global` 的 R 代码块在全局环境中都可以被调用。这将为用户带来更好的启动性能,强烈推荐大家使用。
#### 输入栏
通过添加 `{.sidebar}` 属性设置一个默认布局为左对齐,250 像素宽度的左侧边栏。
在搭建多个页面的仪表盘时,如果想创建一个应用于所有页面的工具条。这时,可以使用一级结构来定义侧边栏。
#### 拓展
下面给出一些学习 Shiny 和创建交互式文档的资源:
1. Shiny 官方网站( http://shiny.rstudio.com) :包含大量的文章、教程和示例。
2. Shiny 网站上的文章“[Introduction to Interactive Documents](http://shiny.rstudio.com/articles/interactive-docs.html)”,这是一个很好的入门指南。
3. 关于部署交互式文档,读者可以使用 Shiny Server 或 RStudio Connect:https://www.rstudio.com/products/shiny/shiny-server/。
## 在 R package 中使用 R Markdown {#package-rmd}
### 写软件包的文档 {#rmd-vignettes}
如果有开发 R 包的经验,或者项目中编写的自定义函数需要清晰的文档和严格的测试,那么可以考虑将项目制作成一个 R 包。如果不知道如何创建一个 R 包,则可以很容易地在 RStudio IDE 中通过点击菜单 `File -> New Project` 开始,选择项目类型为 `R Package`\index{R package!vignette}\index{vignette}。此外,第 \@ref(rmarkdown-package) 节还介绍了如何使用 R Markdown 开发 R 包,可以大大加速 R 包开发过程。
使用 R 包来管理项目有很多好处。例如,可以将数据集放在 `data/` 文件夹中,在 `R/` 下写 R 代码,生成文档到 `man/`(例如,使用 **roxygen2** 包[@R-roxygen2]\index{R package!roxygen2}),并将单元测试添加到 `test/` 中。当涉及到 R Markdown 报告时,可以将它们作为 `vignettes/` 下的软件包长文档来编写。在这些长文档中,可以加载数据集并调用包中的函数。当构建包时(通过 `R CMD build` 或 RStudio 命令),长文档将被自动编译。
要在 R Markdown 中创建一个包的长文档,最简单的方法是通过 RStudio 菜单 `File -> New File -> R Markdown -> From Template`\index{RStudio!vignette template}(见图\@ref(fig:package-vignette))。然后从 **rmarkdown** 包中选择 `Package Vignette`,将得到一个长文档的模板。在更改模板的标题、作者和其他元数据之后,就可以开始编写报告的内容了。
```{r, package-vignette, echo=FALSE, fig.cap='在 RStudio 中创建一个包的长文档。', out.width = "90%"}
knitr::include_graphics('images/package-vignette.png', dpi = NA)
```
或者,也可以安装包 **usethis**\index{R package!usethis} [@R-usethis] 并使用其中的函数 `usethis::use_vignette()`\index{usethis!use\_vignette()} 来创建一个长文档框架。下面是软件包长文档的 YAML 前端内容通常情况下的样子\index{YAML!vignette frontmatter}:
```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_presentation` 和 `tufte::tufte_html`。然而,目前 R 只识别 HTML 和 PDF 格式的长文档。
### R 包中的 R Markdown 模板 {#package-template}
第 \@ref(rmd-vignettes) 节的图 \@ref(fig:package-vignette) 给出了从 **rmarkdown** 包中检索可编辑的 Package Vignette(HTML)模板的过程。这个 R Markdown 文件预先填充了适用于 R 包长文档的元数据。\index{R package!R Markdown template}\index{template!R Markdown}
类似地,任何包都可以包含 R Markdown 模板,包的用户可以通过 RStudio IDE(如图 \@ref(fig:package-vignette) 所示)或使用 `rmarkdown::draft()`\index{rmarkdown!draft} 函数访问这些模板。
#### 模板使用样例
模板是共享自定义结构、样式和内容的有效的方法,很多 R 包都使用了模版文档都方式提高代码的复用率。
许多模板通过预填充 YAML 元数据来添加结构和样式,例如 **rmarkdown** 包的 Package Vignette (HTML)模板。类似地,**rmdformats** 包[@R-rmdformats]提供了许多模板,这些模板将不同的自定义的样式函数传递给 `output` 选项。
其他模板演示了包所需的文档结构。例如,**pagedown** 包[@R-pagedown]包含了许多海报、简历和其他页面布局的模板。类似地,**xaringan** 包的 Ninja Presentation 模板[@R-xaringan]展示了许多不同幻灯片格式选项的语法。
模板也可以展示包的特性和语法。例如,**flexdashboard** 包[@R-flexdashboard]和 **learnr** 包[@R-learnr]都包含了带有代码块的模板,这些代码块分别调用包中的函数来创建示例仪表板或教程。
类似地,模板也可以包含样板内容。例如,**rticles** 包[@R-rticles]提供了许多这样的模板,以使 R Markdown 输出符合不同学术期刊所需的风格和内容指导。样板内容在组织设置中也很有用,比如生成季度报告的团队。
#### 模版设置
**usethis** 包[@R-usethis]可以创建 R Markdown 模版。运行 `usethis::use_rmarkdown_template("Template Name")`\index{usethis!use\_rmarkdown\_template()} 将自动创建所需的目录结构和文件(模板名应自己提供)。
如果想手动设置模板,可以在 `inst/rmarkdown/templates` 目录下创建一个子目录。在这个目录中,需要保存至少两个文件:
1. 一个名为 `template.yaml` 的文件,它为 RStudio IDE 提供了基本的元数据,比如模板的可读名称。这个文件至少应该有 `name` 和 `description` 字段,例如:
```yaml
name: 模板样例
description: 这个模板做了什么
```
如果想在模板被选用时创建一个新目录,可以包含 `create_dir: true`,如果模板依赖于额外的资源,这也是有用的。例如,[**learnr** 包的模板](https://github.com/rstudio/learnr/blob/master/inst/rmarkdown/templates/tutorial/template.yaml) 设置了 `create_dir: true`,而 [**flexdashboard** 包的模板](https://github.com/rstudio/flexdashboard/blob/master/inst/rmarkdown/templates/flex_dashboard/template.yaml)则使用默认的 `create_dir: false`。可以尝试在 RStudio 中打开这两个模板,以注意到不同的用户提示。
2. 保存在 `skeleton/skeleton.Rmd` 下 R Markdown 文档。这可能包含希望放入 R Markdown 文档中的任何内容。
可供选择的是,`skeleton` 文件夹可能还包括其他资源,如模板使用的样式表或图像。这些文件将与模板一起被加载到用户的计算机中。
建立自定义 R Markdown 模板的更多细节,请参阅 [RStudio 扩展](https://rstudio.github.io/rstudio-extensions/rmarkdown_templates.html) 的网站,以及 _R Markdown Definitive Guide_ 的第十七章 [“Document Templates” ](https://bookdown.org/yihui/rmarkdown/document-templates.html)[@rmarkdown2018]。