Mastering shiny(一)基础篇
最近又要写app了,记录一下笔者认为的要点方便以后回顾,主要来自官方的Mastering shiny的Get started部分。PS:这本书习题答案在这里。
1、编写中的要点
1.1 使用响应表达式来减少代码重复
重复的代码,比如反复读取数据,会浪费算力,增加维护难度。传统的做法是单独使用一个变量来捕获input,或者使用函数来捕获计算。但是此处介绍了一个新的方法,即响应表达式——reactive expressions。
基本用法是用reactive({...})函数把你获取变量的代码包起来,他会在需要的时候更新。
server <- function(input, output, session) {
# Create a reactive expression
dataset <- reactive({
get(input$dataset, "package:datasets")
})
output$summary <- renderPrint({
# Use a reactive expression by calling it like a function
summary(dataset())
})
output$table <- renderTable({
dataset()
})
}
1.2 中括号
如果render里有多行代码才需要加中括号。render中应该尽量减少计算。
1.3 UI和server输出的对应关系
textOutput()对应renderText()或renderPrint(),前者输出字符串,后者是R里的代码运行输出格式。
tableOutput()对应renderTable(),dataTableOutput()对应renderDataTable(),前者一般用于小的表格,后者表格可以交互。
plotOutput()对应renderPlot(),可以输出base、ggplot2和plotly等交互图。
2、前端——UI
2.1 Inputs
shiny中有许多不同的输入函数可供使用,如sliderInput()
, selectInput()
, textInput()
, numericInput()
可以生成滑块、选择框、文本框、数值框等,基本上都有现成的,如果基本函数没有,还可以看其他框架如 shinyWidgets, colorpicker, sorttable,这里有人总结了shiny的框架。
● inputId
所有输入函数都有相同的第一个参数
👉它必须是仅包含字母、数字和下划线的简单字符串(不允许使用空格、破折号、句点或其他特殊字符!)。 命名它就像在 R 中命名一个变量一样。
👉它必须是独一无二的。 如果它不是唯一的,你将无法在你的服务器函数中引用这个控件!
● label
大多数输入函数的第二个参数,人类可读的标签,没有字符串限制,由于会展示在前端,所以尽可能让人容易理解。
● value
用于设置缺省值
● 后续的参数就因函数而异
2.2 Outputs
UI中的输出主要是创建一个占位符(需要唯一的id),来和server中的output渲染函数相关联,目前主要有三种输出形式:文本、表格和图。
● plotOutput
默认占据整个宽度,高度400像素,后续可以修改,但是建议res设置为96,可以尽可能看起来和Rstudio里的一致。
还有plot也可以作为输入,plotOutput() 有许多参数,例如 click、dblclick 和 hover。 如果将这些字符串传递给它们,例如 click = "plot_click",将创建一个响应输入 (input$plot_click),可以使用它来处理绘图上的用户交互。
3、响应式编程——Server
input
, output
, session
三个参数,本文只讨论前两个,基本的app可以不涉及session。
3.1 input
● 是一个list,list里的元素根据ui中input函数的id来命名
● 只读因此不能赋值给它,只会根据前端输入变化而变化。
● 必须由renderText() 或 reactive()等函数打包才能读取里面的值,无法直接调用。比如不能写成这样:
server <- function(input, output, session) {
message("The value of input$count is ", input$count)
}
shinyApp(ui, server)
#> Error: Can't access reactive value 'count' outside of reactive consumer.
#> ℹ Do you need to wrap inside reactive() or observer()?
3.2 output
● 也是一个list,list里的元素根据ui中output函数的调用的变量名来命名
● 也必须和render函数一起使用,render函数可以自动追踪输出时使用的输入的变化,而且会把输出转换成html用于展示。
3.3 响应式编程reactive programming
shiny是响应式编程,主要声明每个变量和操作的执行内容,因此:
● lazy,如果变量名写错了,会不执行而不是报错
● 代码的顺序不代表执行顺序,建议画响应图,不管app写的多简单,都应该画响应图,响应图也同时表现了执行的顺序。关于响应图的优化,即尽可能地保证变量单独地更新、不含重复的代码和重复的数据块。减少每一次更新的函数,可以看此文。
server <- function(input, output, session) {
string <- reactive(paste0("Hello ", input$name, "!"))
output$greeting <- renderText(string())
}
3.4 控制响应的方式
shiny基本上是支持实时响应的,原文还介绍了定时刷新的写法。此外,如果执行的计算比较费资源,可以通过eventReactive()设置执行按钮,而不是更改输入就重新计算,即on click。第一个参数指定依赖的变量,第二个参数就是执行的函数,如:
server <- function(input, output, session) {
x1 <- eventReactive(input$simulate, {
rpois(input$n, input$lambda1)
})
x2 <- eventReactive(input$simulate, {
rpois(input$n, input$lambda2)
})
output$hist <- renderPlot({
freqpoly(x1(), x2(), binwidth = 1, xlim = c(0, 40))
}, res = 96)
}
3.5 ObserversobserveEvent和eventReactive用法一样,但是一般是和output相关联的,主要负责一些不影响页面效果的操作,比如下载、连接数据库等,这些往往都是需要实时更新(就是每次都要采用最新的参数进行操作)。
4、如何编写一个app(思路)
就和做很多事情一样,从简到难,一个复杂的app,可以从一个简单的原型慢慢填充上功能后得到,比如我现在想写一个含有各种弹窗、collapse结果的多文件分析应用,就需要这种思路。
● 在写第一个原型时,尽可能地保证app的简单,建议绘制前端和响应图的草图。
● 然后再考虑用户体验,如修改外观,比如表格行数,表格和图排列位置,输入参数是否增加等
评论
发表评论