0

I'm trying to subset data frame in Shiny app based on character input, but only after action button has been pushed. Here how this looks like in UI:

textInput("cases", "Select cases", value = ""),
    actionButton("submit", "Submit", icon = icon("refresh"), 
                 style="float:right"),

The idea is to let user choose what cases of data frame should be shown on the plot (i.e. "11,23,17,102"). In order to convert character vector into numeric, and subsequently subset data frame based on that criteria, I've tried to use next Server code:

    cases <- eventReactive(input$submit, {as.numeric(unlist(strsplit(input$cases,",")))})
    df <- df[cases, ]

After running application and uploading data set I get Error:invalid subscript type 'closure' Without these lines, application works fine on whole data set. Furthermore, the same code, but with non-reactive values works fine outside Shiny environment:

cases <- "3,6,11,55,60" # example of user input 
cases <- unlist(strsplit(cases,","))
cases <- as.numeric(cases)
df <- df[cases, ]

Any help on this would be appreciated.

In response to @socialscientist I am providing a wider scope of my code:

    df <- read.csv(infile$datapath, header = TRUE, stringsAsFactors = FALSE)

    # Subset data frame based on mode selection
    mode <- input$mode
    df <- df[df$Mode == mode, ]
    
    # Take original values of wavelength as x-axis
    df_x <- read.csv(infile$datapath, header = FALSE)
    x_val <- as.vector(df_x[1, 11:2059])
    
    # Remove unnecessary columns
    df <- df[, 11:2059]
    
    # Data frame cases subset based on user input
    df <- eventReactive(input$submit, {
                cases <- as.numeric(unlist(strsplit(input$cases,",")))
                a <- df[cases(), ]
                return(a)
                })
    
    # Transpose and melt data
    t_df <- transpose(df)
    t_df <- cbind(t(x_val), t_df)
    colnames(t_df)[1] <- "wave"
    final_data <- as.data.table(reshape2::melt(t_df, id='wave'))

After running this code I get an error Error in transpose: l must be a list. I am aware that cases() is not function anymore, and that eventReactive now is producing df() function, but I do not know how to use it.

7
  • 1
    Use df[cases(),]. The object stored out of reactive blocks are functions, not simple objects. Commented Aug 15, 2022 at 1:19
  • FYI, a "closure" in R is really just a function with its defining environment. Commented Aug 15, 2022 at 1:19
  • Thank you @r2evans for your brief response. Unfortunately, I didn't get expected result with df[cases(),]. Is there any trick to turn cases() into numeric vector? Commented Aug 15, 2022 at 7:05
  • Best to tell us what you did get Commented Aug 15, 2022 at 7:26
  • I got the comment Error in transpose: l must be a list. I have provided wider scope of my code in addition to the original question. Commented Aug 15, 2022 at 10:41

1 Answer 1

1

I'm not certain if this is going to resolve all issues, but I'm going to infer that the code has nested observe/reactive blocks (or something else equally not-right). Here's a working example of allowing comma-separated numbers and a submit button to subset a frame.

library(shiny)
ui <- fluidPage(
  textInput("cases", "Select cases", value = ""),
  actionButton("submit", "Submit", icon = icon("sync")),
  tableOutput("tbl")
)
server <- function(input, output, session) {
  cases <- eventReactive(input$submit, {
    out <- strsplit(input$cases, ",")[[1]]
    out <- suppressWarnings(as.numeric(out[nzchar(out)]))
    validate(
      need(!anyNA(out), "Input one or more numbers separated by commas")
    )
    out
  })
  output$tbl <- renderTable({
    if (length(cases())) mtcars[cases(),] else mtcars
  })
}
shinyApp(ui, server)

Notes:

  • Error resolution: while out[nzchar(out)] will silently remove empty strings caused by two consecutive commas (e.g., 1,,3) without error, the !anyNA(out) will cause that stop cascading reactivity, instead replacing all components that use cases() with the string Input one or more .... This means that 1,,3 will work without complaint, but 1,a,3 will fail and be politely noisy.

  • This example chooses to show all rows by default. If instead you want nothing shown until there is valid input in cases, then replace the last reactive block here with:

      output$tbl <- renderTable({ mtcars[cases(),] })
    
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you very much for this solution. This solve my problem. I've changed code to subset data set for further plotting to df <- if (length(cases())) df[cases(),] else df. The only thing now is that nothing is happening before action button is pressed, but I can live with that since submitted empty input give me whole data set plotting.
What do you mean, nothing is happening? That's the design of using the button, it is explicitly "do nothing (that depends on this) until it is pressed". Are you hoping that as soon as something is typed in the input box, it is immediately used without the button press?
Thanks for asking. No, I hope that plot appears, with all cases included, soon as I upload data set, and that I can select cases according to this preview by typing and pressing the button. But, as I said, the whole data set can be loaded into plot when I press button with empty text input, and that works fine for me. Thanks again.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.