【问题标题】:Shiny app does not reflect changes in update RData file闪亮的应用程序不反映更新 RData 文件中的更改
【发布时间】:2018-03-24 23:04:35
【问题描述】:

我每天通过 cron 作业为我闪亮的应用程序更新我的 RData 文件。 但是,闪亮的应用程序大部分时间不会选择更新并继续显示旧 RData 文件中的旧数据。

这是最小的可重现示例。从我的桌面执行 data_processing.R 时它工作正常。但是,当它在 Rshiny 服务器上完成时,闪亮的应用程序不会读取更新的日期和时间戳。

data_processing.R

rm(list=ls())
df <- iris
data_update_date_time <- Sys.time()
save.image("working_dataset.RData", compress = TRUE)

server.R

load("working_dataset.RData")

function(input, output, session) {

  # Combine the selected variables into a new data frame
  selectedData <- reactive({
    df[, c(input$xcol, input$ycol)]
  })

  clusters <- reactive({
    kmeans(selectedData(), input$clusters)
  })

  output$plot1 <- renderPlot({
    palette(c("#E41A1C", "#377EB8", "#4DAF4A", "#984EA3",
              "#FF7F00", "#FFFF33", "#A65628", "#F781BF", "#999999"))

    par(mar = c(5.1, 4.1, 0, 1))
    plot(selectedData(),
         col = clusters()$cluster,
         pch = 20, cex = 3)
    points(clusters()$centers, pch = 4, cex = 4, lwd = 4)
  })

  ## Data update date and time stamp
  output$update_date_time <- renderPrint(data_update_date_time)

}

ui.R

pageWithSidebar(
  headerPanel('Iris k-means clustering'),
  sidebarPanel(
    selectInput('xcol', 'X Variable', names(iris)),
    selectInput('ycol', 'Y Variable', names(iris),
                selected=names(iris)[[2]]),
    numericInput('clusters', 'Cluster count', 3,
                 min = 1, max = 9),
    br(),
    h4("Date update date time"),
    textOutput("update_date_time")
  ),
  mainPanel(
    plotOutput('plot1')
  )
)

感谢您抽出宝贵时间。

【问题讨论】:

  • 如果你把load("working_dataset.RData") 放在你的服务器函数中,图像应该在每个新会话中重新加载。服务器函数之外的变量的生命周期几乎是不可预测的 AFAIK。
  • 感谢您的好建议。

标签: r shiny


【解决方案1】:

如果在处理完新的 *.RData 后重新启动闪亮的服务器,它会起作用。我不会把它放到闪亮的服务器中,因为那样

1) 其变量对 UI 不可见

2) 每个打开应用的用户都需要等待 RData 加载完成。

【讨论】:

  • 在 OP 给出的示例中,变量在 ui 中已经无法访问,因为加载语句在 server.R 中,而不是在 global.R 中。除此之外,我同意如果 OP 想从 .RData 图像构建他的 UI,这种方法是最好的。
【解决方案2】:

编辑

shiny 包中实际上有一个名为reactiveFileReader 的函数,它完全符合您的要求:定期检查文件“最后修改”时间或大小是否更改并相应地重新读取。但是,此函数只能在 server 上下文中使用,因此对于连接到您的应用程序的每个用户,该文件将至少读取一次。我的答案中的选项 3 和 4 没有这些低效率。

从这里开始的原始答案

首先,闪亮没有办法跟踪文件更改 AFAIK。您的实现会在任何时候重新加载.RData 文件

  1. shiny-server 通过 bash 或
  2. 重新启动
  3. 全局变量被重新加载,因为应用程序在某个时候变得空闲。

当满足第二个条件时,无法判断。因此,我主张使用以下四个选项之一。从easy你更了解你的闪光点!

选项1:将加载语句放入服务器

在这里,只要有新用户连接应用程序,就会重新加载图像。但是,如果您的 .RData 文件很大,这可能会减慢您的应用程序。如果速度不是问题,我会选择这个解决方案,因为它既简单又干净。

# server.R
function(input, output, session) {
  load("working_dataset.RData")
  ...
}

每当用户刷新页面时,数据也会被重新读取 (F5)

选项 2:在您想重新导入数据时重新启动 shiny-server

另见@shosacos 回答)。这会强制重新加载 .Rdata 文件。

$ sudo systemctl restart shiny-server

同样,这可能会减慢您的生产过程,具体取决于您应用的复杂程度。这种方法的一个优点是如果您在global.R 中加载数据,您还可以使用导入的数据来构建用户界面。 (我假设你没有给出你给出的代码)。

选项3:根据“最后修改”导入

这里的想法是在用户连接到应用程序时检查.RData 是否已更改。为此,您必须使用包含上次导入版本的时间戳的“全局”变量。以下代码未经测试,但应该让您了解如何实现此功能。

# server.R
last_importet_timestamp <- reactiveVal("")

function(input,output,session){
  current_timestamp <- file.info(rdata_path)$mtime 

  if(last_importet_timestamp() != current_timestamp){
    # use parent.frame(2) to make data available in other sessions
    load(rdata_path, envir = parent.fame(2))
    # update last_importet_timestamp
    last_importet_timestamp(current_timestamp) 
  }

  ...
}

在速度方面,这应该比前两个版本更有效。每个时间戳不会多次导入数据(除非闪亮的服务器重新启动或空闲)。

选项 4:导入“reactvely”

基本上,与选项 3 相同,但文件将每 50 毫秒检查一次更改。这是此方法的完整工作示例。请注意,除非检测到“上次修改”的更改,否则不会加载数据,因此产生的开销并不算太糟糕。

library(shiny)

globalVars <- reactiveValues()

rdata_path = "working_dataset.RData"

server <- function(input, output, session){
  observe({
    text = input$text_in
    save(text = text, file = rdata_path, compress = TRUE)
  })
  observe({
    invalidateLater(50, session)
    req(file.exists(rdata_path))
    modified <- file.info(rdata_path)$mtime
    imported <- isolate(globalVars$last_imported)
    if(!identical(imported, modified)){
      tmpenv <- new.env()
      load(rdata_path, envir = tmpenv)
      globalVars$workspace <- tmpenv
      globalVars$last_imported <- modified
    }
  })
  output$text_out <- renderText({
    globalVars$workspace$text
  })
}

ui <- fluidPage(
  textInput("text_in", "enter some text to save in Rdata", "default text"),
  textOutput("text_out")
)

shinyApp(ui, server)

如果您觉得使用globalVars$workspace$text不方便,可以使用with直接访问globalVars$workspace的内容。

  output$text_out <- renderText({
    with(globalVars$workspace, {
      paste(text, "suffix")
    })
  })

【讨论】:

  • 衷心感谢您的超级有用的回答...非常感谢您的宝贵时间!
【解决方案3】:

应用程序由闪亮的服务器缓存,它不会更新应用程序,除非在与闪亮应用程序关联的 .R 文件之一中检测到更改。一个技巧是在您的应用程序代码中进行细微的更改(例如切换库的加载顺序)以提示闪亮的服务器重置缓存,从而包含您更新的 .RData 文件。

在 linux 上你可以简单地执行 touch app.R

【讨论】:

  • 即使在server.R 中更改也没有让服务器选择新更新的应用程序。它仍然会转到旧应用程序。我猜以前的应用程序没有被终止,因为即使从浏览器关闭应用程序后,我也可以看到服务器日志正在更新。似乎该应用程序还活着。
  • 这对我有用,因为更改的 CSV 数据源没有被拾取,但我也注意到在有问题的应用程序中(我要修复,而不是我原来的)数据 在服务器功能之外加载,因此根据此处的其他答案,这将是接下来要检查的事情。
猜你喜欢
  • 2019-01-10
  • 2016-04-10
  • 2017-12-05
  • 2016-04-09
  • 1970-01-01
  • 1970-01-01
  • 2019-01-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多