'How make selectInput() pick up only columns with more than one category/factor in R shiny?
I'm building a Shiny App where I'm uploading multiple files, carry out some operations and output some plots. My plots have a functionality to facet_wrap() by selecting from selectInput(). The selectInput() will refer to the columns in an uploaded dataset and sometimes these columns only have one factor level or a repeated value.
I do not want selectInput() to pick up these columns as faceting or grouping by them makes no difference to the plot (i.e. searching for something that facets or group can get annoying because it's a large dataset). I want to implement an observe() event where the selectInput() only picks up columns with factor levels greater than 1. I have tried using both if statements and ifelse statements but Shiny seems to crash everytime.
The original code is very complex but here's a reproducible example:
library(shiny)
library(ggplot2)
sample <- data.frame(column1 = rep('cat',10), column2 = c(rep('cat',5), rep('dog',5)),
column3 = c(rep('turtle',3), rep('wolf',7)), column4 = rnorm(10))
write.csv(sample, "sample.csv") #Creating sample csv to be ingested in R shiny
ui <- fluidPage(
headerPanel("Webpapp"),
sidebarPanel(
fileInput(inputId = "filedata", #Upload sample.csv here
label = "Upload the Raw Data File",
accept = c("text/csv", "text/comma-separated-values,text/plain",
".csv")),
selectInput("col1", "Select column:", choices = ""), #will be updated in server
),
mainPanel(plotOutput('boxplot')),
)
server <- function(session, input, output) {
data <- reactive({
req(input$filedata)
df <- read.csv(input$filedata$datapath, header = T)
df[1] <- rep('dog', 10)
#I can hardcode it here to delete all columns with less than 2 unique values but prefer not to hard code
list(df = df)
})
observe({
req(data()$df)
updateSelectInput(session, "col1", choices =
list(
if(length(levels(as.factor(data()$df[,1])))>1){
"Column 1" = colnames(data()$df)[1]},
if(length(levels(as.factor(data()$df[,2])))>1){
"Column 2" = colnames(data()$df)[2]},
if(length(levels(as.factor(data()$df[,3])))>1){
"Column 3" = colnames(data()$df)[3]}
)
)
})
output$boxplot <- renderPlot({
ggplot(data()$df, aes(x = as.factor(input$col1), y=column4)) +
geom_boxplot() + geom_point()
})
}
shinyApp(ui = ui, server = server)
Solution 1:[1]
I return two datasets from the reactive
, original_data
which is the data uploaded by the user and filtered_data
which is the data returned for selectInput
.
In ggplot
code using as.factor(input$col1)
would not work directly, use .data
pronoun.
library(shiny)
library(ggplot2)
library(dplyr)
sample <- data.frame(column1 = rep('cat',10), column2 = c(rep('cat',5), rep('dog',5)),
column3 = c(rep('turtle',3), rep('wolf',7)), column4 = rnorm(10))
write.csv(sample, "sample.csv", row.names = FALSE) #Creating sample csv to be ingested in R shiny
ui <- fluidPage(
headerPanel("Webpapp"),
sidebarPanel(
fileInput(inputId = "filedata", #Upload sample.csv here
label = "Upload the Raw Data File",
accept = c("text/csv", "text/comma-separated-values,text/plain",
".csv")),
selectInput("col1", "Select column:", choices = ""), #will be updated in server
),
mainPanel(plotOutput('boxplot')),
)
server <- function(session, input, output) {
data <- reactive({
req(input$filedata)
df <- read.csv(input$filedata$datapath, header = T)
list(original_data = df,
filtered_data = df %>% select(column1:column3) %>% select(where(~n_distinct(.) > 1)))
})
observe({
req(data()$filtered_data)
updateSelectInput(session, "col1", choices = names(data()$filtered_data))
})
output$boxplot <- renderPlot({
req(data()$original_data, input$col1)
ggplot(data()$original_data, aes(x = .data[[input$col1]], y=column4)) +
geom_boxplot() + geom_point()
})
}
shinyApp(ui = ui, server = server)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Ronak Shah |