Reputation: 43
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
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:
(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.
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
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