Shiny apps构建好之后,其运行与反应要快速才会吸引用户;因此,在构建Shiny apps时,耗时计算代码的放置显得尤为重要。
#例子 - stockVis
##创建stockVis Shiny app
- 创文件夹:stockVis
- 下载: app.R 和helpers.R
- 安装quantmod包:install.packages("quantmod")
- 运行:runApp("stockVis")
stockVis应用程序根据股票代码查找股票价格,并将结果显示为折线图。
- 选择要检查的股票
- 选择一系列要回顾的日期
- 选择是否在y轴上绘制股票价格或股票价格的对数,
- 决定是否根据通货膨胀调整价格。
注:注意,“为通货膨胀调整价格”复选框还不能工作。本节的任务之一是修复这个复选框。
默认情况下,stockVis会显示SPY代码(整个标普500的一个指数)。要查找不同的股票,请键入雅虎财经能够识别的股票符号。你可以在这里查看雅虎的股票代码:here。一些常见的符号是GOOG(谷歌)、AAPL (Apple)和GS (Goldman Sachs)。
stockVis非常依赖quantmod包中的两个功能:
- 使用getsymbols直接从雅虎财经(Yahoo finance)和圣路易斯联邦储备银行(Federal Reserve Bank of St. Louis)等网站下载金融数据。
- 使用chartSeries在一个吸引人的图表中显示价格。
- stockVis还依赖于一个名为helpers.R脚本。它包含一个根据通货膨胀调整股票价格的函数。
#复选框和日期范围
stockVis应用程序使用了一些新的小部件:
- 日期范围选择器,使用dateRangeInput
- 用checkboxInput创建的几个复选框。复选框小部件非常简单。当复选框被选中时,它们返回TRUE,当复选框未被选中时返回FALSE。
- 这些复选框在ui对象中被命名为log和adjust,这意味着您可以在服务器函数中将它们作为inputadjust进行查找。
#简化计算
检查当您点击“在对数尺度上绘制y轴”时会发生什么。input$log的值会改变,这会导致renderPlot中的整个表达式重新运行:
output$plot <- renderPlot({
data <- getSymbols(input$symb, src = "yahoo",
from = input$dates[1],
to = input$dates[2],
auto.assign = FALSE)
chartSeries(data, theme = chartTheme("white"),
type = "line", log.scale = input$log, TA = NULL)
})
当renderPlot重新运行时:
- 它使用getSymbols从Yahoo finance重新获取数据
- 它重新绘制图表。
这并不好,因为您不需要重新获取数据来重新绘制绘图。事实上,如果你过于频繁地重新获取数据,雅虎财经(Yahoo finance)会切断你的服务(因为你开始看起来像个爬虫机器人)。但更重要的是,重新运行getSymbols是不必要的工作,这会降低应用程序的速度并消耗服务器。
#响应表达式
可以限制在使用反应表达式的反应期间重新运行的内容。
使用reactive函数创建一个响应表达式,就像渲染render*函数。
例如,下面是一个反应式表达式,它使用stockVis的小部件从Yahoo获取数据。
dataInput <- reactive({
getSymbols(input$symb, src = "yahoo",
from = input$dates[1],
to = input$dates[2],
auto.assign = FALSE)
})
当您运行表达式时,它将运行getSymbols并返回结果。您可以通过调用dataInput()在renderPlot中使用价格数据。
output$plot <- renderPlot({
chartSeries(dataInput(), theme = chartTheme("white"),
type = "line", log.scale = input$log, TA = NULL)
})
响应表达式比常规R函数更聪明一些。它们缓存数据。并知道它们的值何时已经更新。
第一次运行反应表达式时,该表达式将其结果保存在计算机的内存中。下一次调用响应表达式时,它可以返回这个保存的结果,而不进行任何计算(这会使应用程序更快)。
反应表达式只有在知道结果是最新的情况下才会返回保存的结果。如果反应表达式获悉结果已经更新了(因为改变了小部件),表达式将重新计算结果。然后返回新的结果并保存一个新的副本。反应表达式将使用这个新的副本,直到它也更新为止。
reactive()优势:
- 反应式表达式在第一次运行时保存其结果。
- 下一次调用响应表达式时,它检查保存的值是否已更新(即,它所依赖的小部件是否已更改)。
- 值没更新,就可以调用先前保存的值,不进行任何计算;更新了的话,重新运算表达式计算结果。
可以用reactive()来防止Shiny重新运行不必要的代码。考虑一下反应表达式将如何在下面的新stockVis应用程序中工作。
server <- function(input, output) {
dataInput <- reactive({
getSymbols(input$symb, src = "yahoo",
from = input$dates[1],
to = input$dates[2],
auto.assign = FALSE)
})
output$plot <- renderPlot({
chartSeries(dataInput(), theme = chartTheme("white"),
type = "line", log.scale = input$log, TA = NULL)
})
}
当你点击“Plot y axis on the log scale”,输入input$log将改变,renderPlot将重新执行。
- renderPlot将调用dataInput()
- dataInput将检查dates和symb小部件是否没有更改
- dataInput将返回其保存的股票价格数据集,而无需重新从雅虎获取数据
- renderPlot将用选择的的轴表达方式重新绘制图表。
#依赖性
当在Shiny app修改了股票代码,Shiny 也会知道,并且重新画图。
当遇到以下两种情况,Shiny会重新运行:
- 对象的render*函数中的input中的值发生变化,
- 对象的render*函数中的反应性表达式已经更新
可以将反应表达式看作连接,链接input和output中的对象。output中的对象将响应链中任何下游所做的更新。(可以设计一个长链,因为反应表达式可以调用其他反应表达式)
#修复 “Adjust prices for inflation”
现在来修复 “Adjust prices for inflation”,用户就能够根据通货膨胀调整的价格和未调整的价格之间切换。
server <- function(input, output) {
dataInput <- reactive({
getSymbols(input$symb, src = "yahoo",
from = input$dates[1],
to = input$dates[2],
auto.assign = FALSE)
})
output$plot <- renderPlot({
data <- dataInput()
if (input$adjust) data <- adjust(dataInput())
chartSeries(data, theme = chartTheme("white"),
type = "line", log.scale = input$log, TA = NULL)
})
}
#总结
通过使用reactive()模块化代码,可以加快应用程序的速度.
- reactive()接受输入值或其他反应表达式的值,并返回一个新值
- reactive()保存它们的结果,并且只会在输入发生更改时重新计算
- 使用reactive({ })创建响应表达式
- 调用reactive()结果,使用reactive expressions名称加括号
-
- dataInput <- reactive({})的结果调用方法是dataInput();dataInput() 中有多个对象时,使用dataInput()$对象名调用
- 只有在
reactive()
或render*
中可以使用reactive()
#原文:
Use reactive expressions
系列文章:
R shiny教程-1:一个 Shiny app的基本组成部分
R shiny教程-2:布局用户界面
R shiny教程-3:添加小部件到Shiny App
R shiny教程-4:Shiny app响应式结果展示
R shiny教程-5:调用R程序和导入数据
Shiny Server安装