0

I am trying to change the values in 2 ValueBoxes (mean values of attribute 'pf_score' and 'ef_score' every year) with the help of slider, which has years 2008-2016. The output is visible as I wanted, but I am also seeing an error "object of type 'closure' is not subsettable"

UPDATE: I am not able to run entire code by clicking on Run-App. I am getting an error "Could not find function df1". I have to read all data frames first separately and then click Run-App to see the UI.

output screenshot

server.r

require(shiny)
require(dplyr)
require(shinydashboard)

shinyServer(function(input,output){
  
  df <- read.csv("hfi_cc_2018.csv", header = T)
  
  summary(df)
  sapply(df, function(x) sum(is.na(x)))
  #Replace Null Values
  df[is.na(df)] <- 0
  df[,5:ncol(df)] <- round(df[,5:ncol(df)], 2)
  
  #adding selective columns new df1
  #https://stackoverflow.com/questions/10085806/extracting-specific-columns-from-a-data-frame
  df1<- df[, (names(df) %in% c("year","pf_score", "ef_score"
  ))]

output$select_years <- renderUI(
{
   card <- df1 %>%
              filter(year == input$years)
   output$pfrank = renderValueBox(
     valueBox(round(mean(card$pf_score),1),
              "Personal Freedom Score")
   )
   output$efrank = renderValueBox(
     valueBox(round(mean(card$ef_score),1),
              "Economic Freedom Score")
   )
}
)
})

ui.r

require(shiny)
require(shinydashboard)

shinyUI(
  
  dashboardPage( 
    dashboardHeader(title = "Human Freedom Index", titleWidth = 300),
    dashboardSidebar(
      sliderInput("years","Select Year:",
                  min = min(df1$year),
                  max = max(df1$year),
                  value = min(df1$year),
                  step = 1),
      selectInput("variable","Select Freedom Factor:",
                  choices = colnames(df1)
                  )
    ),
    
    dashboardBody(
      uiOutput("select_years"),
      fluidRow(
        valueBoxOutput("pfrank"),
        valueBoxOutput("efrank")
      )
    )
  )
  
)
Community
  • 1
  • 1
Advait Kulkarni
  • 15
  • 1
  • 10

2 Answers2

1

This type of error is discussed in: Error in <my code> : object of type 'closure' is not subsettable

In this case, it looks like you have card as a plain data frame whereas you need a reactive so that it gets recalculated as you move the slider. Also, the expression for renderUI can be simplified to just a list. e.g.,

ui <- shinyUI( ... )
server <- function(input, output) {
  card <- reactive({
    df1 %>%
    filter(year == input$years)
  })
  output$select_years <- renderUI(
    c(renderValueBox(valueBox(round(mean(card()$pf_score), 1),
                   "Personal Freedom Score")),
      renderValueBox(valueBox(round(mean(card()$ef_score), 1),
                   "Economic Freedom Score"))))
}
shinyApp(ui, server)

Also note that the new version of Shiny simplifies the syntax a bit. The code can just go in app.R and you need to define ui and server.

James Brusey
  • 355
  • 3
  • 11
  • This a better solution than the one I posted, I hope @Advait changes the accepted answer to reflect this. @James why does `renderUI({renderValueBox(...); renderValueBox(...)})` and `renderUI({renderValueBox(...)})` both give the 'closure not subsettable' error but `renderUI(c(renderValueBox(...), renderValueBox(...)))` does not? The traceback shows that it fails at line `106: lapply` inside `paste8` but the source code doesn't help. So I assumed it had something to do with `renderValueBox` being a reactive expression, which is a closure? – user51462 Jun 22 '19 at 03:25
  • `renderUI` arguments can take a number of different forms but I think the thing to remember here is that you are setting up a data structure that is going to be returned as part of `output`. The first example that you give evaluates inside the `{...}` and then gives the last expression result to `renderUI`. So this is not so good because it you probably wanted both `renderValueBox` expressions to be included. I think the issue with the closure is that you are passing something that should either be a reactive or a data structure - not a closure. – James Brusey Jun 22 '19 at 06:44
0

You could use an observe() to render the value boxes instead of renderUI:

require(shiny)
require(dplyr)
require(shinydashboard)

   df <- read.csv("hfi_cc_2018.csv", header = T)

   summary(df)
   sapply(df, function(x) sum(is.na(x)))
   #Replace Null Values
   df[is.na(df)] <- 0
   df[,5:ncol(df)] <- round(df[,5:ncol(df)], 2)

   #adding selective columns new df1
   #https://stackoverflow.com/questions/10085806/extracting-specific-columns-from-a-data-frame
   df1 <- df[, (names(df) %in% c("year","pf_score", "ef_score"))]

 #UI  
 ui <- dashboardPage( 
    dashboardHeader(title = "Human Freedom Index", titleWidth = 300),
    dashboardSidebar(
      sliderInput("years","Select Year:",
                  min = min(df1$year),
                  max = max(df1$year),
                  value = min(df1$year),
                  step = 1),
      selectInput("variable","Select Freedom Factor:",
                  choices = colnames(df1)
      )
    ),

    dashboardBody(
      fluidRow(
        valueBoxOutput("pfrank"),
        valueBoxOutput("efrank")
      )
    )
  )

#Server
 server <- function(input,output){

   observe({
       card <- df1 %>%
         filter(year == input$years)
       output$pfrank = renderValueBox(
         valueBox(round(mean(card$pf_score),1),
                  "Personal Freedom Score")
       )
       output$efrank = renderValueBox(
         valueBox(round(mean(card$ef_score),1),
                  "Economic Freedom Score")
       )
     })
 }

#Run app
shinyApp(ui, server)
user51462
  • 1,658
  • 2
  • 13
  • 41
  • thank you so much. It worked. Can you please explain what was the purpose of observe ? – Advait Kulkarni Jun 02 '19 at 07:21
  • No problem! `observe()` listens to the value of `input$years` and re-executes the expression inside it each time `input$years` changes. `observeEvent(`input$years`, {...})` would give the same result. The expression inside a `render_` function must return a Shiny tag object, HTML, or a list of such objects, whereas the object being returned by the `renderUI` in your question is `output$efrank` which contains the code needed to update the value box, i.e. a reactive expression, which defines a closure. I might be mistaken but I think that's where the error was coming from. – user51462 Jun 02 '19 at 10:19
  • PS Global variables like `df` should be defined outside the ui/server functions or in a `global.R` file in your app directory. In my answer, I've defined it just before the UI call. – user51462 Jun 02 '19 at 10:20