對(duì)于一些小應(yīng)用而言,隨便寫寫的Shiny應(yīng)用的速度其實(shí)也能湊合。但是如果你的應(yīng)用涉及到很多的計(jì)算,那么如何降低不必要的運(yùn)行,提高運(yùn)行效率呢?
準(zhǔn)備
創(chuàng)建一個(gè)stockVis
文件夾,在其中下載如下兩個(gè)文件:
之后在stockVis
的同級(jí)目錄下運(yùn)行runApp("stockVis")
,網(wǎng)頁會(huì)出現(xiàn)如下內(nèi)容
這個(gè)APP可以根據(jù)提供的股票縮寫(Symbol)查詢指定日期范圍內(nèi)的價(jià)格變動(dòng)(Date Range)。我們需要在此基礎(chǔ)上完善后面兩個(gè)勾選框的功能。
代碼不足
原來的server
函數(shù)長(zhǎng)下面這個(gè)樣子:
server <- function(input, output) {
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)
})
}
根據(jù)「R shiny基礎(chǔ)」在shiny應(yīng)用中加載數(shù)據(jù)和腳本里提到的,每次用戶發(fā)生交互式,render部分的代碼就會(huì)重新運(yùn)行一次,也就是說上面的代碼會(huì)在每次用戶做了修改之后,就得重新獲取數(shù)據(jù),然后進(jìn)行作圖。假如有一個(gè)參數(shù)是調(diào)整標(biāo)題的,那么每調(diào)整一次標(biāo)題就得重頭運(yùn)行一遍,顯然這中間獲取數(shù)據(jù)可以省去。
代碼優(yōu)化
shiny提供了reactive
函數(shù),類似于render
類函數(shù),接受R的表達(dá)式作為輸入,但是它只會(huì)在原始的控件(widgets)發(fā)生變化之后才會(huì)更新結(jié)果。那么本次案例中就是將原本在renderPlot
獲取在線獲取股票數(shù)據(jù)這一步代碼移動(dòng)到reactive
表達(dá)式中
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)
})
reactive函數(shù)會(huì)緩存原本的結(jié)果,當(dāng)運(yùn)行時(shí)發(fā)現(xiàn)輸入并沒有發(fā)生改變時(shí)就不重復(fù)計(jì)算。
總結(jié)下reactive
函數(shù)的運(yùn)行步驟:
- 當(dāng)你第一次運(yùn)行
reactive
函數(shù)后,他會(huì)在內(nèi)存中存儲(chǔ)結(jié)果 - 下一次運(yùn)行時(shí),他會(huì)檢查保存的值是否過期(也就是該數(shù)據(jù)依賴的輸入是否發(fā)生變化)
- 如果數(shù)據(jù)過期,那么重新計(jì)算,然后更新內(nèi)存中保存的結(jié)果
- 如果數(shù)據(jù)未過期,那么直接返回保存的結(jié)果,不做任何計(jì)算。
到此,Shiny的基礎(chǔ)知識(shí)點(diǎn)就結(jié)束了,后面我得寫幾個(gè)應(yīng)用來練練手。
傳送門
Shiny基礎(chǔ)教程: