Dynamically rbind data.tables in reactive environment

Hello

I have a list of data tables which all have the same structure and from which I need to dynamically choose a subset and aggregate them to a single data.table by using e.g. rbind.
In a non reactive environment this is no problem, but when I use dynamic input it does not seem to create the data table.
I managed to rbind two data tables, but with more the do.call(rbind,.. ) does not seem to work.

I tried to create a reproducible example:

### Create an exemplary list of data tables as original data set
somelist<-list()
somelist[[1]] <- as.data.table(list("A"=c(1,1),"B"=c(2,2),"C"=c(3,3)))
somelist[[2]] <- as.data.table(list("A"=c(4,4),"B"=c(5,5),"C"=c(6,6)))
somelist[[3]] <- as.data.table(list("A"=c(7,7),"B"=c(8,8),"C"=c(9,9)))
somelist[[4]] <- as.data.table(list("A"=c(10,10),"B"=c(11,11),"C"=c(12,12)))
somelist[[5]] <- as.data.table(list("A"=c(13,13),"B"=c(14,14),"C"=c(15,15)))
biglist <- do.call("rbind",somelist) #rbinding in this way works in a non reactive environment


server <- function(input,output,session) {
  
  output$somelist<- renderTable({biglist})
  output$testinput1<-renderText({input$testinput1})
  
    y<-reactive({input$testinput1}) #Input the amount of data tables that should be appended
        
    testlist<-reactive({list(y())}) #create empty list
    # testlist<-list() #create empty list  
    # Create a dynamic subset(testlist) of data.tables from the original data 
    reactive({
      for(i in 1:y())
      {
        testlist()[[i]]<-reactive({somelist[[i]]})
      }
    })
    # Rbind the chosen data tables into one
    reactivelist<-reactive({do.call("rbind",testlist())}) #create appended data.table from list of data.tables
        
    output$testtable<-renderTable({
      reactivelist()
    })
      
    Sum_A <- reactive({reactivelist()[,sum(A)]})
      
    output$sum_a<-renderText({
        a<-paste0(Sum_A())
        a})  
      
    

}

header<-dashboardHeader(title = "Rbind_test",titleWidth = 280)

sidebar<-dashboardSidebar(width = 280,sidebarMenu(id="sidebar_tabs",
                                                  menuItem("AAA", tabName = "AAA")
                                                  ))


body<-dashboardBody(title="Main",
                    inlineCSS(list(.blue   = "color: blue",
                                   .red  = "color: red")),
                      tabItem(tabName = "Overview",h1("Overview"),
                      fluidPage(
                      box(sliderInput(inputId = "testinput1",label="testinput1",min=1,max=5,value=5)),
                      box(title="Output1",textOutput(outputId="testinput1")),
                      box(title="Output2: rbind of first x lists",tableOutput(outputId="testtable")),
                      box(title="rbinded_table",tableOutput(outputId="somelist")),
                      box(title="Value from table (sum A)",textOutput(outputId="sum_a"))
                    )                  
                    )
                    )

ui <- dashboardPage(skin = "black",
                        header,
                        sidebar,
                        body
)

shinyApp(ui = ui, server = server)

Where am I going wrong?

Thank you for the reprex. Here is one way using reactiveValue to store the value and observe to recalculate each time the input change.

library(shiny)
library(shinydashboard)
library(data.table)
library(shinyjs)

### Create an exemplary list of data tables as original data set
somelist<-list()
somelist[[1]] <- as.data.table(list("A"=c(1,1),"B"=c(2,2),"C"=c(3,3)))
somelist[[2]] <- as.data.table(list("A"=c(4,4),"B"=c(5,5),"C"=c(6,6)))
somelist[[3]] <- as.data.table(list("A"=c(7,7),"B"=c(8,8),"C"=c(9,9)))
somelist[[4]] <- as.data.table(list("A"=c(10,10),"B"=c(11,11),"C"=c(12,12)))
somelist[[5]] <- as.data.table(list("A"=c(13,13),"B"=c(14,14),"C"=c(15,15)))
biglist <- do.call("rbind",somelist) #rbinding in this way works in a non reactive environment

server <- function(input,output,session) {
  output$somelist<- renderTable({biglist})
  output$testinput1<-renderText({input$testinput1})
  res <- reactiveValues()
  observe({
    table_list <- somelist[seq_len(input$testinput1)]
    res$DT <- do.call("rbind", table_list)
    res$Sum_A <- res$DT[, sum(A)]
  })
  output$testtable<-renderTable({
    res$DT
  })
  output$sum_a <- renderText({
    paste0("res: ", res$Sum_A)
  })  
}

header <- dashboardHeader(title = "Rbind_test",titleWidth = 280)
sidebar <- dashboardSidebar(width = 280,sidebarMenu(id="sidebar_tabs",
                                                  menuItem("AAA", tabName = "AAA")))

body<-dashboardBody(title="Main",
                    inlineCSS(list(.blue   = "color: blue",
                                   .red  = "color: red")),
                    tabItem(tabName = "Overview",h1("Overview"),
                            fluidPage(
                              box(sliderInput(inputId = "testinput1",label="testinput1",min=1,max=5,value=5)),
                              box(title="Output1",textOutput(outputId="testinput1")),
                              box(title="Output2: rbind of first x lists",tableOutput(outputId="testtable")),
                              box(title="rbinded_table",tableOutput(outputId="somelist")),
                              box(title="Value from table (sum A)",textOutput(outputId="sum_a"))
                            )                  
                    )
)

ui <- dashboardPage(skin = "black",
                    header,
                    sidebar,
                    body
)

shinyApp(ui = ui, server = server)

What is important is to understand the reactivity mechanism. You can put R code inside reactive() or observe inside get into reactive context to call input$testinput1 and other expression from a reactive context.

The documentation can help you :
http://shiny.rstudio.com/articles/#reactivity

4 Likes

Hi cderv

Thank you very much for your reply.

In my real problem I have a big multidimensional list (originallist). Where I need to specify very specific data tables, which I want to take out. The call with [seq_len()] does not work there. Thats why I used a foor loop so I would be able to add the tables one by one to another list that in turn can be used to do an aggregation by rbind.

In your reply you used:

table_list <- somelist[seq_len(input$testinput1)]

But I need something where I can call the slots more specifically, thats why I used a for loop.
When I replace it with the following, the solution ceases to work

y<-reactive({input$testinput1})
for (i in 1:y()){
      table_list[[i]]<-somelist[[i]] #or originallist[[i]][[j]] for my real problem
      }

I don't believe values will be stored back to a reactive object by doing reactiveObj()[[i]] <- val. If it is a reactive, only the reactive expression can update the value of the object.

Looking at your dependencies within the server function if y is updated, everything is reset.


A code change must happen for somelist. somelist should not be defined as a reactive inside a reactive call. It should be defined somewhere outside a reactive call (most likely inside the server function). The examples below have it as static, regular R lists. If it is a reactive list that changes, the code blocks below need to be updated to somelist()[[i]] to retrieve the reactive value at run time.


I see two ideas going forward...

  1. Move the reactive code block inside the creation of testlist
testlist <- reactive({
  if (is.null(y())) return(list())
  ret <- list()
  # populate the list...
  for (i in 1:y()) {
    ret[[i]] <- somelist[[i]]
  }
  # return the list
  ret
})
reactivelist <- reactive({ do.call("rbind", testlist()) })
  1. If you do not need access to testlist for anything else, I would absorb reactivelist and testlist into one.
reactivelist <- reactive({
  ret <- list()
  if (!is.null(y())) {
    # populate the list...
    for (i in 1:y()) {
      ret[[i]] <- somelist[[i]]
    }
  }
  # rbind the list
  do.call("rbind", ret)
})
2 Likes

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.