2014-06-12 29 views
7

想知道是否有技巧/方法可以緩存通過我們的閃亮應用生成的情節。在R/Shiny中緩存情節

背景:

我們正在做的有些計算密集型計算,其最終導致的情節。我已經緩存(使用memoise)完成的計算,全局閃亮,但它仍然需要大約0.75秒來渲染一個情節。我只是想知道我們是否可以通過消除渲染圖像所需的時間來減少這些時間,以及是否已經做到了這一點。

更多細節:

我使用網格建立在這種情況下,圖(熱圖理想的情況下想緩存將基於磁盤的存儲在內存中地塊不會擴大

感謝。 ! -Abhi

+0

查看'?renderImage'上的例子,它可能會給你一些想法。基本上你想要一個記憶劇情功能,返回一個PNG文件,我想;並使用renderImage來調用該memoized函數。 –

+0

謝謝喬。關於我們如何能夠自動縮放通過renderImage渲染的靜態圖像的任何想法。 – Abhi

回答

4

假設你正在使用ggplot(其具有光澤,我敢打賭是一個合理的假設)。

  1. 創建一個空的列表來存儲你的grob,說Plist
  2. 當用戶請求一個圖表,創建基於閃亮輸入的字符串哈希
  3. 檢查是否圖表已經保存,如hash %in% names(Plist)
  4. 如果是的話,成爲了該圖形
  5. 如果沒有,生成圖中,GROB保存到列表中,命名由哈希,如元素,Plist[hash] <- new_graph
+0

感謝您的快速回復。我正在使用網格..只是想知道grob是什麼意思,並且在內存中存儲多個圖可能會很昂貴,而且我希望緩存在服務器重新啓動時保持不變。任何將圖塊緩存到磁盤並從那裏渲染的軟件包? – Abhi

+0

也想知道你是否知道任何可以根據函數的輸入創建字符串哈希的包。這對我的情況來說很方便。 – Abhi

+0

'grob'只是一個'圖形對象'。您可以像保存其他R對象一樣將它們保存到磁盤。 (參見'saveRDS')。但是,從磁盤加載它們可能需要從頭開始計算它們。 –

2

裏卡多薩波塔答案是非常好,是我用來解決類似的問題,但我想添加一個代碼解決方案。

對於緩存我使用digest::digest()其中我只是將該特定圖的參數列表添加到該函數以創建哈希字符串。我最初認爲我必須從observe()中提取散列字符串,然後使用if/else語句來確定是否應該根據圖像是否曾經創建過,根據其是否發送到renderImage()renderPlot()。我與這個混淆了一段時間,然後偶然發現只使用renderImage()。它不是一個完美的圖像替代,但對於本演示的目的而言足夠接近。

ui.R

library(shiny) 

fluidPage(
    sidebarLayout(
    sidebarPanel(
     sliderInput("bins", 
        "Number of bins:", 
        min = 1, 
        max = 50, 
        value = 25), 
     selectInput("plot_color", "Barplot color", 
        c("green"="green", 
         "blue"="blue")) 
    ), 
    mainPanel(
     plotOutput("distPlot", width='100%', height='480px') 
    ) 
) 
) 

和server.R

library(shiny) 

function(input, output) { 

base <- reactive({ 
    fn <- digest::digest(c(input$bins, input$plot_color)) 
    fn}) 

output$distPlot <- renderImage({ 
    filename <- paste0(base(), ".png") 
    if(filename %in% list.files()){ 
     list(src=filename) 
    } else { 
    x <- faithful[, 2] 
    bins <- seq(min(x), max(x), length.out = input$bins + 1) 
    png(filename) 
    hist(x, breaks = bins, col = input$plot_color, border = 'white') 
    dev.off() 
list(src=filename) 
    } 

    }, deleteFile = FALSE) 
} 
0

雖然兩者這個問題的答案都非常好,我想也添加使用shiny modules另一個。以下模塊將繪圖函數和它的參數的反應版本作爲輸入。最後do.call(plotfun, args())用於創建情節。

library(shiny) 

cachePlot <- function(input, output, session, plotfun, args, width = 480, height = 480, 
         dir = tempdir(), prefix = "cachedplot", deleteonexit = TRUE){ 
    hash <- function(args) digest::digest(args) 

    output$plot <- renderImage({ 
    args <- args() 
    if (!is.list(args)) args <- list(args) 
    imgpath <- file.path(dir, paste0(prefix, "-", hash(args), ".png")) 

    if(!file.exists(imgpath)){ 
     png(imgpath, width = width, height = height) 
     do.call(plotfun, args) 
     dev.off() 
    } 
    list(src = imgpath) 
    }, deleteFile = FALSE) 

    if (deleteonexit) session$onSessionEnded(function(){ 
    imgfiles <- list.files(tempdir(), pattern = prefix, full.names = TRUE) 
    file.remove(imgfiles) 
    }) 
} 

cachePlotUI <- function(id){ 
    ns <- NS(id) 
    imageOutput(ns("plot")) 
} 

正如我們所看到的,模塊刪除,如果需要創建的圖像文件,並給出情況下,需要持續的緩存來使用自定義緩存目錄(因爲它是在我的實際用例)的選項。

有關使用示例,我將使用與Stedy類似的hist(faithful[, 2])示例。

histfaithful <- function(bins, col){ 
    message("calling histfaithful with args ", bins, " and ", col) 
    x <- faithful[, 2] 
    bins <- seq(min(x), max(x), length.out = bins + 1) 
    hist(x, breaks = bins, col = col, border = 'white') 
} 

shinyApp(
    ui = fluidPage(
    inputPanel(
     sliderInput("bins", "bins", 5, 30, 10, 1), 
     selectInput("col", "color", c("blue", "red")) 
    ), 
    cachePlotUI("cachedPlot") 
), 
    server = function(input, output, session){ 
    callModule(
     cachePlot, "cachedPlot", histfaithful, 
     args = reactive(list(bins = input$bins, col = input$col)) 
    ) 
    } 
)