Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R Shiny: Use reactiveValues() with data.table assign-by-reference

I have a shiny app in which more than one reactive component uses the same result from a function that is slow to calculate. To avoid calculating the slow function more than once, I can use reactiveValues() to recalculate the function when its inputs change, and make the result available to all reactive components that require it.

But, if the reactiveValues object is a data.table, and I update it using :=, shiny does not detect the change, and the outputs that rely on it do not get updated.

Is there any way to use data.table assign by reference either with reactiveValues or another way that avoids recalculating the function multiple times.

Here is a reproducible example using data.table assign-by-reference in which output$result2 fails to get updated when the input changes:

library(shiny)
library(data.table)
library(plotly)

ui = fluidPage(
  sidebarLayout(
    sidebarPanel(
      sliderInput('x1', 'x1', min=0, max=2, value=1, step=0.1)
    ),
    mainPanel(
      plotlyOutput('result1'),
      verbatimTextOutput('result2')
    )
  )
)

server = function(input, output) {
  values <- reactiveValues()
  values$dt = data.table(v1 = 1:100, v2 = 1)

  slow.func = function(my.dt, x) {
    Sys.sleep(2)
    my.dt[, v2 := v1^x]
  }

  output$result1 = renderPlotly({
    values$dt = slow.func(values$dt, input$x1)
    plot_ly(values$dt) %>%
      add_lines(x = ~v1, y = ~v2)
  })      
  output$result2 = renderText({
    paste('Final value =', values$dt[.N, v2])
  })  
}
shinyApp(ui = ui, server = server)

For comparison, here is a version of the server function using standard assignment of data.frames, which does perform as expected:

server = function(input, output) {
  values <- reactiveValues()
  values$dt = data.frame(v1 = 1:100, v2 = 1)

  slow.func = function(my.dt, x) {
    my.dt$v2 = my.dt$v1^x
    Sys.sleep(2)
    my.dt
  }

  output$result1 = renderPlotly({
    values$dt = slow.func(values$dt, input$x1)
    plot_ly(values$dt) %>%
      add_lines(x = ~v1, y = ~v2)
  })

  output$result2 = renderText({
    paste('Final value =', values$dt[100,]$v2)
  })  
}
like image 301
dww Avatar asked May 19 '17 16:05

dww


1 Answers

say you have defined a reactive variable table_updated so you can increment it by one each time the slow function is done. Other values/plots will only need to observe table_updated.

Actually the actionButton(see description section) does the same thing, every time it gets clicked, its value is incremented by 1.

values <- reactiveValues(table_updated = 0)

slow.func = function(my.dt, x) {
  # do stuff
  values$table_updated <- values$table_updated + 1
}

output$result2 = renderText({
  values$table_updated
  paste('Final value =', values$dt[100,]$v2)
})  
like image 83
lkq Avatar answered Oct 19 '22 18:10

lkq