Jasmine Dogu
Jasmine Dogu

Reputation: 43

R Shiny Error: Warning: Error in $: object of type 'closure' is not subsettable

I am using the following code and I always get this subsettable error unless if I run the line

df <- read.csv("./world-happiness-report-cleaned.csv") manually before running the app. What am I subsetting, and where am I wrong? I can't seem to find the error, and I'm super new to Shiny so I've never had to deal with this before. Thank you so much!!

This link is to a filebin that has the csv I used: https://filebin.net/wjctohctz1sxm16y

server.R

# Elit Jasmine Dogu, ejd5mm
# Project One DS 3002
library(dplyr)
library(countrycode)
library(shiny)
df <- read.csv("./world-happiness-report-cleaned.csv")

    #saveRDS(df, "./df.RDS")
server <- function(input, output) {
    #reading in the data and basic data cleaning 
    #df<- read.csv("world-happiness-report-cleaned.csv")
    #df <<- readRDS("./df.RDS")
    #df <- read.csv("./world-happiness-report-cleaned.csv")
    
    # Filter data based on user selections
    output$table <- DT::renderDataTable(DT::datatable({
        data <- df %>%
            filter(
                if(input$year != "All") {
                    Year ==input$year
                } else {TRUE}
            ) %>%
            filter(
                if(input$country != "All") {
                    Country ==input$country
                } else {TRUE}
            ) %>%
            filter(
                if(input$continent != "All") {
                    Continent ==input$continent
                } else {TRUE}
            )
        
        return(data)
        
    }))
        # Generate a summary of the dataset (on the left panel)
        output$summary <- renderPrint({
            data <- df %>%
                filter(
                    if(input$year != "All") {
                        Year ==input$year
                    } else {TRUE}
                ) %>%
                filter(
                    if(input$country != "All") {
                        Country ==input$country
                    } else {TRUE}
                ) %>%
                filter(
                    if(input$continent != "All") {
                        Continent ==input$continent
                    } else {TRUE}
                )
            
            return(summary(data))
            
        })
    
       #Generate a function to show the number of rows w/ any given dataframe selection/restriction
    rows = function() {
        data <- df %>%
            filter(
                if(input$year != "All") {
                    Year ==input$year
                } else {TRUE}
            ) %>%
            filter(
                if(input$country != "All") {
                    Country ==input$country
                } else {TRUE}
            ) %>%
            filter(
                if(input$continent != "All") {
                    Continent ==input$continent
                } else {TRUE}
            )
        
        return(nrow(data)) #returns number of rows of the data
    }
    
    #Generate a function to show the number of columns w/ any given dataframe selection/restriction
    cols = function() {
        data <- df %>%
            filter(
                if(input$year != "All") {
                    Year ==input$year
                } else {TRUE}
            ) %>%
            filter(
                if(input$country != "All") {
                    Country ==input$country
                } else {TRUE}
            ) %>%
            filter(
                if(input$continent != "All") {
                    Continent ==input$continent
                } else {TRUE}
            )
        
        return(ncol(data)) #returns the number of columns of the data
    }

    #Using the functions created above
        output$columns  <- renderText({
            paste("Number of Columns:" , cols() ) #text to display the number of columns
        })
        output$rows  <- renderText({
            paste("Number of Rows (Records):" , rows() ) #text to display the number of rows 
        })

        output$data_ex  <- renderText({
            paste("Please see README.md file for information regarding the dataset.") #text to display where to find more information
        })
 

        # Downloadable csv of selected dataset 
        output$downloadData <- downloadHandler(
            filename = function() {
                selected <-c() #this assists with the name of the file
                if (input$year != "All") { 
                    selected <-c(selected, input$year)
                }
                if (input$country != "All") {
                    selected <-c(selected, input$country)
                }
                if (input$continent != "All") {
                    selected <-c(selected, input$continent)
                }
                if (length(selected) == 0) {
                    selected <- c("AllData")
                }
                
                paste0(paste(selected, collapse="-"), ".csv")
            },
            content = function(con) {
                data <- df %>%
                    filter(
                        if(input$year != "All") {
                            Year ==input$year
                        } else {TRUE}
                    ) %>%
                    filter(
                        if(input$country != "All") {
                            Country ==input$country
                        } else {TRUE}
                    ) %>%
                    filter(
                        if(input$continent != "All") {
                            Continent ==input$continent
                        } else {TRUE}
                    )
                write.csv(data, con, row.names = TRUE) #saves the filtered data
            }
        )
}

ui.R

# Elit Jasmine Dogu, ejd5mm
# Project One DS 3002
library(shiny)
library(shinyWidgets)

ui <- fluidPage(
    #text with project name and my information
    titlePanel("World Happiness Report"),
    tags$h3("DS 3002- Project One"),
    tags$h4("Elit Dogu, ejd5mm 3rd Year UVA"),
    # use a gradient in background, setting background color to blue
    setBackgroundColor(  
        #https://rdrr.io/cran/shinyWidgets/man/setBackgroundColor.html used this website for help on background color
        color = c("#F7FBFF", "#2171B5"),
        gradient = "radial",
        direction = c("top", "left")
    ),
    # Sidebar layout with input and output definitions ----
    sidebarLayout(
        
        # Sidebar panel for inputs ----
        sidebarPanel(
            
            # Output: Header + summary of distribution ----
            h4("Summary"),
            verbatimTextOutput("summary"),
            
            # Download button
            downloadButton("downloadData", "Download")
        ),
    # Create a new Row in the UI for selectInputs
    # Main panel for displaying outputs ----
    mainPanel(

    fluidRow(  #manipulates the original dataframe given user selection
        column(4,
               selectInput("year",  #selection for the year variable
                           "Year:",
                           c("All",
                             unique(as.numeric(df$Year))))
        ),
        column(4,
               selectInput("country",  #selection for the country variable
                           "Country:",
                           c("All",
                             unique(as.character(df$Country))))
        ),
        column(4,
               selectInput("continent",  #selection for the continent variable
                           "Continent:",
                           c("All",
                             unique(as.character(df$Continent))))
        )
    ),
    # Create a new row for the table
    DT::dataTableOutput("table"),
    
    # Create a new column for the text to be displayed
    column(12,
           verbatimTextOutput("columns") #column to display col count
    ),
    column(12,
           verbatimTextOutput("rows") #column to display row count
    ),

    column(12,
           verbatimTextOutput("data_ex") #column to display more information text
            )
        )
    )
)

Thank you!!

Upvotes: 0

Views: 4649

Answers (2)

r2evans
r2evans

Reputation: 160437

Up front: StéphaneLaurent's answer is the first thing you need to fix. Below is not causing that error, though I still recommend the changes for other reasons.


In your rows and cols functions, you are accessing input$ directly. This is wrong for at least two reasons:

  1. (general functional programming) Your functions are breaching scope, reaching out to things they were not explicitly passed. This can be a bit about programming style, but functions that use variables not explicitly passed to it can be difficult to troubleshoot.

  2. input$ can only be accessed from within a reactive*, observe*, or render* block (i.e., something that is shiny-reactive). Nothing outside any of those should try to do anything with input$ or output$.

As a fix, make the functions agnostic to shiny by making them self-contained and just working scalars/vectors. (I'll also reduce the logic a little.)

  #Generate a function to show the number of rows w/ any given dataframe selection/restriction
  rows = function(year, country, continent) {
    data <- df %>%
      filter(
        year == "All" | year == Year,
        country == "All" | country == Country,
        continent == "All" | continent == Continent
      )
    return(nrow(data)) #returns number of rows of the data
  }

  # ...

  output$rows  <- renderText({
    paste("Number of Rows (Records):" , rows(input$year, input$country, input$continent) )
  })

Frankly, your cols function is a little odd ... you can change the number of rows of a frame all day long, but the number of columns does not change. Unless you dplyr::select some columns away, it should always be exactly ncol(df).

As for the reduction of logic, your if statements embedded within your dplyr::filter chain aren't wrong, but I think the more R-idiomatic way to do it is what I've suggested. In your case, if a variable is "All", then it returns a single TRUE, which dplyr::filter applies to all rows. If not, then it returns a logical vector (1 for each row) indicating if the frame's variable matches the selected input.

In my version, I do something very similar: the first year == "All" will still resolve to a single logical (assuming year, from input$year), but the right-hand-side will be as long as the number of rows. You can test what this looks like:

TRUE | c(T,F,T,F)
# [1] TRUE TRUE TRUE TRUE
FALSE | c(T,F,T,F)
# [1]  TRUE FALSE  TRUE FALSE

Upvotes: 1

St&#233;phane Laurent
St&#233;phane Laurent

Reputation: 84529

The problem is that you are using df$...... inside the UI. If you define df inside the server function, it is not defined in the UI. So you get this error because R recognizes df as the function provided by the 'stats' package (an object of type "closure" is a function).

Upvotes: 3

Related Questions