Shiny程序包学习要点

全文PDF Shiny.pdf

\1. 什么是Shiny?

\2. Shiny App的基本结构

2.1 运行Shiny App

2.2 app.R文件

​ 2.2.1 ui用户界面

​ 2.2.2 server函数

​ 2.2.3 shinyApp

2.3 运行shiny程序包中的例子

\3. 信息的输入与输出

\4. 响应式编程与html标签

\5. 编写高效的ShinyApp

1. 什么是Shiny?

Shiny 是Rstudio公司开发的R程序包。最早于2012年12月出现在CRAN上。

长期以来,R编写图形界面都不是很方便,Shiny在这方面进行了很大努力。Shiny在R中定义了网页中多种对象,因而可用其制作与R互动的网页。在Shiny生成的网页中,用户可提交数据,调整参数,控制生成的图形,如地图、箱线图等,也可以控制要显示的结果,如表格,模型拟合结果等。

想要在本地运行和调试用Shiny编写的用户界面,就要先安装Shiny及其依赖的程序包。Shiny应用程序可以部署在服务器端, 用户只要访问相应的网址,就可直接对网页中的对象进行操作。安装在服务器上的Shiny Server会收集信息并控制网页如何响应。

例1. 安装 Shiny程序包

1
install.packages("shiny")

2. Shiny App的基本结构

一个文件夹,加上包含Shiny命令的app.R文件,再加上用到的数据文件和R脚本等, 就称为ShinyApp。

Shiny App总是由三部分组成: 1. 文件夹 2. app.R的脚本 3. 其他数据和R脚本等

文件夹的名字就是shinyApp的名称,因此最好不要有英文或数字以外的字符。

2.1 运行Shiny App

shinyApp需要在导入Shiny程序包之后运行。

1
2
3
4
## 在本地运行shiny app
setwd("/Users/jinlong/Desktop/")
library(shiny)
runApp("learn_shiny")

2.2 app.R文件

app.R 文件必须包含三部分: 1. ui 设定图形界面 2. server函数 3. 调用shinyApp的命令 shinyApp(ui, server)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 一个最简单的app.R脚本
library(shiny)# Define UI ----
ui <- fluidPage(

)

# Define server logic ----
server <- function(input, output) {

}

# Run the app ----

shinyApp(ui = ui, server = server)

2.2.1 ui用户界面

其中ui定义网页中对象的展示方式,包括文字的字体,字号,颜色,排列方式,以及各种组件的默认参数,可以选择的参数等。

2.2.2 server函数

server函数读取组件中收集到的数据,计算后,再传递给UI。

2.2.3 shinyApp

shinyApp(ui, server) 分别调用ui和server函数,生成网页。

shiny程序包中内置了十几个例子, 通过以下方式运行:

2.3 运行shiny程序包中的例子

1
2
library(shiny)
runExample("01_hello")

用户还可以查看其它Shiny模版, 参见https://rstudio.github.io/shinythemes/

3. 信息的输入与输出

例1

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
# Define UI for app that draws a histogram ----
# ui为user interface的缩写,ui是基本组件之一。此处定义ui,用于生成频度直方图
ui <- fluidPage(
# App title ----
# 添加标题
titlePanel("Hello Shiny!"),
# Sidebar layout with input and output definitions ----
# 添加侧面的导航栏, 同时定义输入input和输出的显示模式
sidebarLayout(
# Sidebar panel for inputs ----
# 输入数据用的导航栏
sidebarPanel(
# Input: Slider for the number of bins ----
# Input: 定义直方图的数量
sliderInput(inputId = "bins",
label = "Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Main panel for displaying outputs ----
# 网页的主要板块用于输出结果
mainPanel(
# Output: Histogram ----
# 输出结果:频度直方图
plotOutput(outputId = "distPlot")
)
)
)
# 定义server函数,绘制频度直方图
server <- function(input, output) {
# server函数同时包括input和output两个参数
# 输出结果要保存在output参数中

# output中,用来保存绘图结果
# 返回给putput中的对象,是经过 renderPlot转换过的R脚本
# renderPlot本身是一个函数, 内部却直接放花括号
# 花括号内的R脚本, 与普通R脚本无异
output$distPlot <- renderPlot({

x <- faithful$waiting
bins <- seq(min(x), max(x), length.out = input$bins + 1)

hist(x, breaks = bins, col = "#75AADB", border = "white",
xlab = "Waiting time to next eruption (in mins)",
main = "Histogram of waiting times")

})

}
# 脚本必须以shinyApp(ui, server)结束。shinyApp(ui, server)

要点

  • shiny application 包括 UI 和 Server以及shinyApp(ui, server)三部分。
  • UI负责收集数据和展示数据。fluidPage中的参数,titlePanel, sidebarLayout, mainPanel用“,”分隔。
  • 收集数据的一般在 SidePanel 中进行
  • 展示数据在MainPanel中进行
  • 所有网页内容要放在fluidPage当中
  • UI中对象的命名习惯是,首字母小写,第二个单词的首字母大写,中间无任何间隔符号 (例如下划线_),这与javaScript的命名习惯相同
  • titlePanel用来给整个程序命名
  • sidebarLayout主要用来控制Sidebar独立为一列
  • 任何要输入的内容一般都放在sidebarPanel
  • 要输出的内容放在mainPanel
  • 输出的内容需要放在plotOutput中,以生成动态图形
  • 输入的内容需要用 outputId 指定
  • server 必须为一个函数,参数为input和output
  • 所有server函数要输出的内容,都必须作为output的组件,创建output组件用$指明即可
  • 所有从ui界面的数据获取,计算,绘图等都需要在renderPlot中处理
  • renderPlot的调用方法, 看似一个函数, 但是在小括号内放花括号,可放置多行R代码。
  • 从UI提取的数据,是input的参数的组件,用input$xxx提取
  • shinyApp结尾, 必须用shinyApp()`` 指明ui和server。格式为shinyApp(ui = ui, server = server)`

例2

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
library(shiny)
# Define UI for dataset viewer app ----
# 定义图形界面,用于查看数据
ui <- fluidPage( # App title ----
# 添加标题
titlePanel("Shiny Text"),
# Sidebar layout with a input and output definitions ----
# 添加导航栏,并定义input和output
sidebarLayout(
# Sidebar panel for inputs ----
# 用于输入数据的导航栏
sidebarPanel(
# Input: Selector for choosing dataset ----
# Input: 输入数据选择器
selectInput(inputId = "dataset",
label = "Choose a dataset:",
choices = c("rock", "pressure", "cars")),
# Input: Numeric entry for number of obs to view ----
# Input:输入要查看的记录条数
numericInput(inputId = "obs",
label = "Number of observations to view:",
value = 10)
), # Main panel for displaying outputs ----
# 主显示版
mainPanel(
# Output: Verbatim text for data summary ----
# Output: 直接显示数据概要
verbatimTextOutput("summary"),
# Output: HTML table with requested number of observations ----
# Output: 显示指定行数的表格
tableOutput("view")

)
)
)

# Define server logic to summarize and view selected dataset ----
# 定义server函数,用于汇总和查看所选择数据
server <- function(input, output) {
# Return the requested dataset ----
# 返回所请求的数据
datasetInput <- reactive({
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
})
# Generate a summary of the dataset ----
# 创建数据汇总summary
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})
# Show the first "n" observations ----
# 显示前n个观测值
output$view <- renderTable({
head(datasetInput(), n = input$obs)
})

}

# Create Shiny app ----
# 创建ShinyAppshinyApp(ui = ui, server = server)

要点

  • 每一个形为*Input的组件,如 selectInputnumericInput 都是R函数,可以在帮助文件查询到各参数的要求。
  • 在ui中,输入数据可以用selectInput组件建立,此时用inputId参数识别输入的数据。
  • 输入数据可以用numericInput组件建立,此时用inputId识别输入的数据
  • selectInputnumericInput组件在输入数据时, 都有inputId, label以及value三个选项
  • 直接输出要打印的数据,可以用verbarimTextOutput("")
  • 其中“”中放要显示的对象名称,这个对象是由server中的output参数返回的,如“summary”
  • 要输出表格, 用tableOutput("view")
  • server函数中,通过reactive提取input$dataset中的数据
  • reactive面对的是selectInput组件,则需要用switch对UI中相应的操作做出响应
  • reactive的结果保存为一个对象, 也是在括号内放花括号, 以保证读取多行
  • 要输出的结果, 都必须保存到output对象中, 保存结果的操作符为 操作符为$
  • 要输出的结果分别用renderPrint, 或者renderTable转换为Print或者Table相应的类型。即使是一行,也需要用花括号将要打印的值或者表格包
  • datasetInputreactive返回的对象,但是也是一个函数,用来获取UI界面获取的值, 所以调用时, 都用datasetInput(), 与调用函数时一样
  • server文件中, 输入的数值, 可以直接用input$obs 的方式提取。
  • 所有要输出的内容, 都是作为output的一部分, 用$指定并赋值。这是响应式编程的基本要求。

4. 响应式编程与html标签

例3

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
# Define UI for dataset viewer app ----
# 定义ui, 该app用于查看数据
ui <- fluidPage(
# App title ----
# App标题
titlePanel("Reactivity"),
# Sidebar layout with input and output definitions ----
# 导航栏
sidebarLayout(
# Sidebar panel for inputs ----
# 输入数据的导航栏
sidebarPanel(
# Input: Text for providing a caption ----
# Note: Changes made to the caption in the textInput control
# are updated in the output area immediately as you type

# Input: 选择的标题会马上显示在右侧的结果显示区域中

textInput(inputId = "caption",
label = "Caption:",
value = "Data Summary"),
# Input: Selector for choosing dataset ----
# Input: 输入数据选择器
selectInput(inputId = "dataset",
label = "Choose a dataset:",
choices = c("rock", "pressure", "cars")),
# Input: Numeric entry for number of obs to view ----
# Input:设定要查看的记录条数
numericInput(inputId = "obs",
label = "Number of observations to view:",
value = 10)

), # Main panel for displaying outputs ----
# 显示输出结果的主要面板

mainPanel(
# Output: Formatted text for caption ----
# Output: 修改标题格式
h3(textOutput("caption", container = span)),
# Output: Verbatim text for data summary ----
# Output: 打印data summary
verbatimTextOutput("summary"),
# Output: HTML table with requested number of observations ----
# Output: 生成HTML表格,以显示指定数量的观测值
tableOutput("view")

)
)
)

要点

  • shiny中,可以直接使用html5标签
  • h3 为 第三级标题的html标签
  • tableOutput 直接生成表格

例4

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
library(shiny)

# Define UI for dataset viewer app ----
ui <- fluidPage(
# App title ----
titlePanel("More Widgets"),
# Sidebar layout with input and output definitions ----
sidebarLayout(
# Sidebar panel for inputs ----
# 导航栏
sidebarPanel(
# Input: Select a dataset ----
# Input: 选择数据
selectInput("dataset", "Choose a dataset:",
choices = c("rock", "pressure", "cars")),

# Input: Specify the number of observations to view ----
# Input: 查看多少条记录
numericInput("obs", "Number of observations to view:", 10),
# Include clarifying text ----
# 帮助
helpText("Note: while the data view will show only the specified",
"number of observations, the summary will still be based",
"on the full dataset."),

# Input: actionButton() to defer the rendering of output ----
# until the user explicitly clicks the button (rather than
# doing it immediately when inputs change). This is useful if
# the computations required to render output are inordinately
# time-consuming.
# 当获得结果所需时间较长, 是否运行脚本需要由用户决定, 此时用本按钮。

actionButton("update", "Update View")

),

# Main panel for displaying outputs ----
# 主面板
mainPanel(
# Output: Header + summary of distribution ----
# 输出
h4("Summary"),
verbatimTextOutput("summary"),
# Output: Header + table of distribution ----
# 输出
h4("Observations"),
tableOutput("view")
)

)
)# Define server logic to summarize and view selected dataset ----
server <- function(input, output) {
# Return the requested dataset ----
# Note that we use eventReactive() here, which depends on
# input$update (the action button), so that the output is only
# updated when the user clicks the button
datasetInput <- eventReactive(input$update, {
switch(input$dataset,
"rock" = rock,
"pressure" = pressure,
"cars" = cars)
}, ignoreNULL = FALSE)

# Generate a summary of the dataset ----
output$summary <- renderPrint({
dataset <- datasetInput()
summary(dataset)
})

# Show the first "n" observations ----
# The use of isolate() is necessary because we don't want the table
# to update whenever input$obs changes (only when the user clicks
# the action button)

output$view <- renderTable({
head(datasetInput(), n = isolate(input$obs))
})

}# Create Shiny app ----shinyApp(ui, server)

要点

  • helpText 用来显示SideBarPanel中的帮助文本
  • actionButton("update", "Update View") 是按钮, 前一个参数是动作, 后一个参数是按钮上的标签。
  • 使用eventReactive 捕获UI按钮中的动作。
  • 使用Widgets时, 需要指定名称以及标签, 名称用于访问,标签用于显示。名称一般全部小写。标签一般首字母大写。Widgets标签也是可以直接用html5的tag的。 无论是 chekboxGroupInput 还是 radioButtons, selectInput, 都涉及到choices参数, 输入的都是list()
  • helpText()用以显示灰色帮助文字

server 函数有两个参数,inputoutput, output表示运算的结果, input表示从Widget收集到的数据。

输入数据,在UI中, 使用*Output系列函数。

Shiny中可以直接使用html5的标签, 包括:

Shiny函数 html标签 说明
p <p> A paragraph of text
h1 <h1> A first level header
h2 <h2> A second level header
h3 <h3> A third level header
h4 <h4> A fourth level header
h5 <h5> A fifth level header
h6 <h6> A sixth level header
a <a> A hyper link
br <br> A line break (e.g. a blank line)
div <div> A division of text with a uniform style
span <span> An in-line division of text with a uniform style
pre <pre> Text ‘as is’ in a fixed width font
code <code> A formatted block of code
img <img> An image
strong <strong> Bold text
em <em> Italicized text

UI中可放入如下Widgets

按钮 功能
checkboxGroupInput A group of check boxes
checkboxInput A single check box
dateInput A calendar to aid date selection
dateRangeInput A pair of calendars for selecting a date range
fileInput A file upload control wizard
helpText Help text that can be added to an input form
numericInput A field to enter numbers
radioButtons A set of radio buttons
selectInput A box with choices to select from
sliderInput A slider bar
submitButton A submit button
textInput A field to enter text

Shiny输出的对象类型, 函数名称及其返回的对象

Shiny输入的对象 Shiny返回的对象
dataTableOutput DataTable
htmlOutput raw HTML
imageOutput image
plotOutput plot
tableOutput table
textOutput text
uiOutput raw HTML
verbatimTextOutput text
  • \*Output可以放在 sidebarPanelmainPanel 中。在server中,任何一个widget都需要有自己的名字,这个名字用来从server函数内的input对象访问widget。 Output系列函数,用来直接读取server 函数中, output对方中保存的内容。
  • server 函数返回的是output对象。
  • widget 显示出的结果, 都作为output的子对象保存。 可以返回的类型
  • server函数中,要显示的内容,都是通过render* 函数输出的。包括以下类型s
render 函数 生成的对象
renderDataTable DataTable
renderImage images (saved as a link to a source file)
renderPlot plots
renderPrint any printed output
renderTable data frame, matrix, other table like structures
renderText character strings
renderUI a Shiny tag object or HTML

每一个render*函数,输入的参数都是用花括号包裹的。可以包含一行至多行代码。 数据文件放入data子文件夹。

5. 编写高效的ShinyApp

一般来说,运行ShinyApp是,ShinyApp本身只运行一次; Server函数,每一个用户访问时,都会运行一次; render* 函数里面的内容,用户一旦改变内容, 就会重新运行一次。

因此,为了Shiny程序能够高效运行, 一般有以下建议:

在server函数一开头,就要加载所有函数,程序包以及读取数据;

server函数中, 尽量在render* 函数意外建立新对象;

render* 函数中, 只放不可或缺的对象。

需要从网络下载的数据,一般放入 reactive({})环境中。

当数据发生变化时,R会自动更新。

reactive({}) 返回的也是函数, 其返回值要用render\* 显示时,后面要跟括号()。

注意: 只能在render\*系列中调用reactive expressions。

参考网址