sp29
sp29

Reputation: 383

Use reactively made Shiny objects as inputs to another reactive events

My shiny app renders a dynamic data frame using the DT library. User can select the desired rows by clicking. Once the rows are selected the app displays and download the transposed rows in the form of the data frame. However, in doing so (displays and download) I encounter an error as follows-

Error Warning: Error in [.data.frame: undefined columns selected [No stack trace available]

Here are the steps in details

  1. The data fetched (Say from some Website)
  2. User Select the rows by clicking onto them and then click on the actionButton() to mark them as the 'first group'
  3. The user clears the previous selection and repeats the same steps as 'Step -2' for making a second group.
  4. The clean() function will transpose the data frame (Rownames will become column names) and then subset the data frame based on user-selected row.names().

Reproducible code with added comments

library(shiny)
library(DT)

temp_func <- function(){
  x <- mtcars
  y = x[,1]
  return(list(complete_df = as.data.frame(x), column1 = as.data.frame(y)))
}

clean <- function(df, g1, g2){
  x1 <- (mtcars)
  df_g1 <- subset(df, select = as.vector(g1))
  df_g2 <- subset(df, select = as.vector(g2))
  df_new <- cbind(df_g1, df_g2)
  return(as.data.frame(df_new))
}

# UI
ui <- shinyUI({
  fluidPage(
    actionButton("fetch", label = "Step1-Fetch data first"),
    DT::dataTableOutput("table"),br(),
    tags$h5("Selected for Group-1"), verbatimTextOutput("Group1"),br(),
    tags$h5("Selected for Group-2"),verbatimTextOutput("Group2"),hr(),br(),
    tags$h4("Follow Steps: "),
    actionButton("selG1", label = "Step-2: Mark as  Group-1"),
    actionButton("clear", label = "Step-3: Clear Selection"),
    actionButton("selG2", label = "Step-4: Mark as Group-2"),
    actionButton("cleanIt", "Step-5: Clean Dataframe"),
    DT::dataTableOutput("table2"),
    downloadButton("down", "Step-6: Download cleaned dataframe")
    )})

# Server
server <- Main_Server <- function(input,output,session){
  
  # Reactive Values
  values <- reactiveValues(table = NULL)
  group1 <- reactiveValues(Group1 = NULL)
  group2 <- reactiveValues(Group2 = NULL)
  clean_df <- reactiveValues(df = NULL)
  
  # fetchEvent (Consider temp_func() is fetching data from website)
  observeEvent(input$fetch, {values$table <- temp_func()})
  
  # Rendering table for display
  output$table <- renderDT({datatable(values$table$complete_df)})
  
  # Selection Event (Gorup1)
  observeEvent(input$selG1, {group1$Group1 <- rownames(values$table$complete_df[as.numeric(input$table_rows_selected),])})
  
  # Reset selections
  observeEvent(input$clear, {output$table <- renderDT({datatable(values$table$complete_df)})})
  
  # Selection Event (Gorup1)
  observeEvent(input$selG2, {group2$Group2 <- rownames(values$table$complete_df[as.numeric(input$table_rows_selected),])})
  
  # Print Events
  output$Group1 <- renderPrint({group1$Group1})
  output$Group2 <- renderPrint({group2$Group2})
  
  # observeEvent
  observeEvent(input$cleanIt, {clean_df$df <- clean(values$table$complete_df, group1$Group1, group2$Group2)
  })
  
  # Rendering table for display
  output$table2 <- renderDT({datatable(clean_df$df$df_new)})
  
   # Combined Download
  output$down <- downloadHandler(
    filename = function() { "File.csv"},
    content = function(file) {write.csv(clean_df$df$df_new, file)})
  
}

# Run-app
shinyApp(ui, server)

Upvotes: 1

Views: 467

Answers (1)

starja
starja

Reputation: 10365

Your subsetting was wrong; g1 and g2 are rownames, but you used it in subset which expects columnnames (therefore the error message you've got). Therefore I've changed the clean function. Your error message is not very informative because you don't have a stack trace. If you want to get a stack trace, you should use the Shiny App template in RStudio (see here at the end of 6.2.1). With this, I get

Warnung: Error in [.data.frame: undefined columns selected
  78: stop
  77: [.data.frame
  75: subset.data.frame
  73: clean [C:\Users\jhage\Documents\Programmierung\Shiny_tests\app_2\shinytest7/app.R#12]
  72: observeEventHandler [C:\Users\jhage\Documents\Programmierung\Shiny_tests\app_2\shinytest7/app.R#63]
   1: runApp

so it's easier to debug.

The corrected code:

library(shiny)
library(DT)

temp_func <- function(){
  x <- mtcars
  y = x[,1]
  return(list(complete_df = as.data.frame(x), column1 = as.data.frame(y)))
}

clean <- function(df, g1, g2){
  df_selection <- df[c(which(rownames(df) == g1), which(rownames(df) == g2)),  ]
  df_new <- t(df_selection)
  return(as.data.frame(df_new))
}

# UI
ui <- shinyUI({
  fluidPage(
    actionButton("fetch", label = "Step1-Fetch data first"),
    DT::dataTableOutput("table"),br(),
    tags$h5("Selected for Group-1"), verbatimTextOutput("Group1"),br(),
    tags$h5("Selected for Group-2"),verbatimTextOutput("Group2"),hr(),br(),
    tags$h4("Follow Steps: "),
    actionButton("selG1", label = "Step-2: Mark as  Group-1"),
    actionButton("clear", label = "Step-3: Clear Selection"),
    actionButton("selG2", label = "Step-4: Mark as Group-2"),
    actionButton("cleanIt", "Step-5: Clean Dataframe"),
    DT::dataTableOutput("table2"),
    downloadButton("down", "Step-6: Download cleaned dataframe")
  )})

# Server
server <- Main_Server <- function(input,output,session){
  
  # Reactive Values
  values <- reactiveValues(table = NULL)
  group1 <- reactiveValues(Group1 = NULL)
  group2 <- reactiveValues(Group2 = NULL)
  clean_df <- reactiveValues(df = NULL)
  
  # fetchEvent (Consider temp_func() is fetching data from website)
  observeEvent(input$fetch, {values$table <- temp_func()})
  
  # Rendering table for display
  output$table <- renderDT({datatable(values$table$complete_df)})
  
  # Selection Event (Gorup1)
  observeEvent(input$selG1, {group1$Group1 <- rownames(values$table$complete_df[as.numeric(input$table_rows_selected),])})
  
  # Reset selections
  observeEvent(input$clear, {output$table <- renderDT({datatable(values$table$complete_df)})})
  
  # Selection Event (Gorup1)
  observeEvent(input$selG2, {group2$Group2 <- rownames(values$table$complete_df[as.numeric(input$table_rows_selected),])})
  
  # Print Events
  output$Group1 <- renderPrint({group1$Group1})
  output$Group2 <- renderPrint({group2$Group2})
  
  # observeEvent
  observeEvent(input$cleanIt, {clean_df$df <- clean(values$table$complete_df, group1$Group1, group2$Group2)
  })
  
  # Rendering table for display
  output$table2 <- renderDT({datatable(clean_df$df$df_new)})
  
  # Combined Download
  output$down <- downloadHandler(
    filename = function() { "File.csv"},
    content = function(file) {write.csv(clean_df$df$df_new, file)})
  
}

# Run-app
shinyApp(ui, server)

BTW: Have a look at the selection argument of datatable, you can set it so that only one row can be selected, then you don't need the "undo" buttons in your app.

Upvotes: 1

Related Questions