【问题标题】:caching plots in R/Shiny在 R/Shiny 中缓存绘图
【发布时间】:2014-08-03 06:15:15
【问题描述】:

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

背景:

我们正在进行一些计算密集型计算,最终产生了一个绘图。我已经在缓存(使用 memoise)完成的计算,全局闪亮,但渲染绘图仍需要大约 0.75 秒。我只是想知道我们是否可以通过删除渲染图像所需的时间来减少该时间,以及是否有巧妙的方法已经这样做了。

更多细节:

我正在使用网格来创建绘图(在这种情况下为热图。理想情况下,希望缓存是基于磁盘的,因为将绘图存储在内存中不会扩大规模。

谢谢! -阿比

【问题讨论】:

  • ?renderImage的例子,它可能会给你一些想法。基本上你想要一个返回PNG文件的记忆绘图函数,我想;并使用 renderImage 调用该记忆函数。
  • 谢谢乔。关于如何让我们通过 renderImage 渲染的自动缩放静态图像变得闪亮的任何想法。

标签: r shiny shiny-server


【解决方案1】:

假设您使用的是ggplot(我敢打赌,对于 Shiny,这是一个公平的假设)。

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

【讨论】:

  • 感谢您的快速回复。我为此使用网格..只是想知道 grob 是什么意思,并且在内存中存储多个图可能会很昂贵,而且我希望缓存在服务器重新启动时保持不变。任何将绘图缓存到磁盘并从那里渲染它们的包?
  • 还想知道您是否知道任何可以根据函数的输入创建字符串哈希的包..这对我的情况来说很方便。
  • grob 只是一个graphical object。您可以像任何其他 R 对象一样将它们保存到磁盘。 (见?saveRDS)。但是,从磁盘加载它们可能与从头计算它们一样长。
【解决方案2】:

编辑

自闪亮 1.2.0 起支持缓存使用 renderPlot()/plotOutput() 创建的图像。

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

output$plot <- renderCachedPlot(
  expr = {
    histfaithful(bins = input$bins, col = input$col) 
  },
  cache = diskCache()
)

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

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

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

原答案

虽然这个问题的两个答案都很好,但我还是想使用shiny modules 添加另一个答案。以下模块将 plotfunction 和它的参数的反应版本作为输入。最后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"))
}

正如我们所见,如果需要,该模块会删除创建的图像文件,并提供使用自定义缓存目录的选项,以防需要持久缓存(就像在我的实际用例中一样)。

对于一个用法示例,我将使用 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))
    )
  }
)

【讨论】:

    【解决方案3】:

    Ricardo Saporta 的回答非常好,我曾经解决过类似的问题,但我也想添加一个代码解决方案。

    对于缓存,我使用了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')
        )
      )
    )
    

    和服务器.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)
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-08-20
      • 2018-06-24
      • 2021-11-02
      • 1970-01-01
      • 2021-12-14
      • 2020-07-23
      • 1970-01-01
      相关资源
      最近更新 更多