在R/Shiny中缓存情节

Abh*_*bhi 10 r shiny shiny-server

只是想知道是否有技巧/方法可以缓存通过我们闪亮的应用程序生成的图.

背景:

我们正在做一些计算密集型计算,最终导致绘图.我已经缓存(使用memoise)完成的计算,全局闪亮,但渲染绘图仍需要大约0.75秒.我只是想知道我们是否可以通过消除渲染图像所花费的时间以及是否有光滑的方式来减少时间.

更多细节:

我正在使用网格来创建绘图(在这种情况下是热图.理想情况下,将缓存设置为基于磁盘,因为在内存中存储绘图不会扩展.

谢谢!-Abhi

Ric*_*rta 6

假设你正在使用ggplot(使用Shiny,我敢打赌这是一个公平的假设).

  1. 比如说,创建一个空列表来存储你的grob Plist.
  2. 当用户请求图形时,根据闪亮输入创建字符串哈希
  3. 检查图表是否已保存,例如 hash %in% names(Plist)
  4. 如果是,请提供该图表
  5. 如果不是,则生成图形,将grob保存到列表中,通过哈希命名元素,例如, Plist[hash] <- new_graph


Gre*_*lia 5

编辑

renderPlot()/plotOutput()从闪亮的1.2.0版本开始,支持使用创建的图像进行缓存。

以下解决方案的行为类似于的以下用法renderCachedPlot()

output$plot <- renderCachedPlot(
  expr = {
    histfaithful(bins = input$bins, col = input$col) 
  },
  cache = diskCache()
)
Run Code Online (Sandbox Code Playgroud)

renderCachedPlot()允许使用合理的默认值在内存和磁盘上进行缓存。可以自定义生成哈希键的规则,默认情况下,该规则用于digest::digest()中显示的所有反应式表达式expr

下面的解决方案演示了如何使用闪亮的模块来实现这些功能的子集(在磁盘上缓存)。基本策略是使用

  • digest::digest() 根据发送到绘图函数的参数创建缓存键
  • do.call()将参数传递给plot函数,除非从创建的键digest()表示图像已被缓存
  • grDevices::png()从调用中捕获图像do.call()并将其添加到缓存
  • shiny::renderImage() 从缓存中提供图像

原始答案

尽管对这个问题的两个答案都很好,但我想使用闪亮的模块添加另一个答案。以下模块将plot函数及其参数的反应形式作为输入。最终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(dir, pattern = prefix, full.names = TRUE)
    file.remove(imgfiles)
  })
}

cachePlotUI <- function(id){
  ns <- NS(id)
  imageOutput(ns("plot"))
}
Run Code Online (Sandbox Code Playgroud)

如我们所见,该模块删除了需要时创建的图像文件,并提供了在需要持久缓存的情况下使用自定义缓存目录的选项(在我的实际用例中)。

对于一个用法示例,我将hist(faithful[, 2])像Stedy一样使用该示例。

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))
    )
  }
)
Run Code Online (Sandbox Code Playgroud)