Error: cannot coerce type 'closure' to vector of type 'character' - Shiny R

Viewed 2157

I am having a problem with putting reactive elements into a function. I am trying to produce plots based on user input - when the user selects values from three sets of checkboxes they should get a number of plots in return. The way this is supposed to work is that the plots are named as combinations of the user inputs, so when the user selects certain boxes they should see the graphs they have asked for. I'm doing this using the grepl function to determine if the user inputs match with certain graphs. For example, if the user inputs "Marine", "Speciation" and "Temperature change", that will correlate with "mari", "spec" and "temp", which I can then evaluate against plot_list to output the desired graph(s) (in this case just "plot_mari_spec_temp"). The checkboxes work and a list of inputs are sent to server but when I try to use these inputs in the grepl function to determine whether they are in the plots, the server doesn't run and I get this error:

Warning: Error in as.vector: cannot coerce type 'closure' to vector of type 'character'
  52: as.character.default
  50: grepl
  49: server [#13]
Error in as.vector(x, "character") : 
  cannot coerce type 'closure' to vector of type 'character'

I believe the issue is that the reactive elements are parsed as functions which grepl can't handle, but I do not know how to change it so that I can compare the inputs with the outputs and make the graphs display. I have tried using isolate() but it isn't working.

I haven't started working on actually outputting the graphs yet, which is why mainPanel looks so empty. The plan is to have a for loop to output each graph, but I have to get past this first.

Code:

UI:

library(shiny)
# Define UI for Pseudosuchia app
ui <- pageWithSidebar(

  # App title
  headerPanel("Pseudosuchia Diversification"),

  # Three sidebar panels for the three inputs
  sidebarPanel(
    checkboxGroupInput("habitat_choice", "Habitat:",
                c("Marine" = "mari",
                  "Terrestrial" = "terr"), selected = c("mari")),
    checkboxGroupInput("div_type_choice", "Diversification type:",
                c("Speciation" = "spec",
                  "Extinction" = "exti")),
    checkboxGroupInput("factor_choice", "Abiotic/Biotic Factor:",
                c("Temperature Change" = "temp",
                  "Sea Level Change" = "sl",
                  "Self Diversification" = "ltt",
                  "Dinosauria Diversification" = "dino")),
  ),
  # Main panel for displaying the plot output
  mainPanel(
    plotOutput("my_plot")
  )
)

Server:

# Define server logic to plot the three variables against diversification rates
server <- function(input, output) {
  #Each variable is pasted together here to generate an output of one of the existing plots using the inputs above.
  #For example, if the user selects "Marine", "speciation" and "Temperature Change" in the input menus then the output would be evaluated as `plot_mari_spec_temp` which prompts rendering of the corresponding plot.
    count <- 0
    allowed_list <- c()
    plot_list <-  c("plot_terr_spec_temp", "plot_terr_exti_temp", "plot_mari_spec_temp", "plot_mari_exti_temp",
                  "plot_terr_spec_sl", "plot_terr_exti_sl", "plot_mari_spec_sl", "plot_mari_exti_sl",
                  "plot_terr_spec_ltt", "plot_terr_exti_ltt", "plot_mari_spec_ltt", "plot_mari_exti_ltt",
                  "plot_terr_spec_dino", "plot_terr_exti_dino", "plot_mari_spec_dino", "plot_mari_exti_dino")
    input_list <- c(reactive(input$habitat_choice), reactive(input$div_type_choice), reactive(input$factor_choice))
    for(this_plot in plot_list){
      for(input in input_list){
      if(grepl(isolate(input), this_plot, fixed = T)){
          count = count+1
        }
      }
      if(count ==3){
        allowed_list <- append(allowed_list, this_plot)
      }
    }

  for(this_plot in allowed_list){
    output$my_plot <- renderPlot({eval(parse(text = this_plot))})
  }
}

shinyApp(ui, server)
1 Answers

In my opinion, all of this is based on wrong assumptions about how shiny works and many things should be changed in your code. See @Limey comment.

However, I'm also taking into account that I may missing something and shouldn't be so radical with my comment, so let's try to fix the error you mentioned. I would say, generally it is because you do not treat reactive as a function. If we have:

my_fun <- reactive({
a <- 1
a
})

Then to access this value we need to use my_fun as a function, i.e.

output$my_output <- renderPrint({
my_fun()
})

It will print 1.

In your code, you iterate over few reactives, which is not necessary (nota bene), but this is not a cause of problem. Problem is that you use input instead of input(). But I also found that even if it should work theoretically, it doesn't work in reality. I think it is because of this unfortunate variable name input, as this is used in Shiny as a name of list where are stored values from user inputs. Let's change the name into inputX and use inputX() inside grepl():

server <- function(input, output) {
  #Each variable is pasted together here to generate an output of one of the existing plots using the inputs above.
  #For example, if the user selects "Marine", "speciation" and "Temperature Change" in the input menus then the output would be evaluated as `plot_mari_spec_temp` which prompts rendering of the corresponding plot.
  count <- 0
  allowed_list <- c()
  plot_list <-  c("plot_terr_spec_temp", "plot_terr_exti_temp", "plot_mari_spec_temp", "plot_mari_exti_temp",
                  "plot_terr_spec_sl", "plot_terr_exti_sl", "plot_mari_spec_sl", "plot_mari_exti_sl",
                  "plot_terr_spec_ltt", "plot_terr_exti_ltt", "plot_mari_spec_ltt", "plot_mari_exti_ltt",
                  "plot_terr_spec_dino", "plot_terr_exti_dino", "plot_mari_spec_dino", "plot_mari_exti_dino")
  input_list <- c(reactive(input$habitat_choice), reactive(input$div_type_choice), reactive(input$factor_choice))
  for(this_plot in plot_list){
    for(inputX in input_list){
      if(grepl(isolate(inputX()), this_plot, fixed = T)){
        count = count+1
      }
    }
    if(count ==3){
      allowed_list <- append(allowed_list, this_plot)
    }
  }
  
  for(this_plot in allowed_list){
    output$my_plot <- renderPlot({eval(parse(text = this_plot))})
  }
}

Now the error is fixed, but another one appears - check out the console. I think it is related to the fact that there is no default values for div_type_choice and factor_choice and no default values means NULL in this case, so you are getting the same as in this example:

grepl(NULL, "a")
Related