Reputation: 19
I am facing a hard time with this shiny app and I was hoping I could get some help here. The data is mtcars I just added a column to represent a fictional amount of money. The flow is basically this:
1.- The user selects a variable they want to group_by ('cyl','vs','am','gear','carb'), then a table will show on screen with the data already grouped by the variable that the user selected, the user can group by multiple variables.
2.- After that if they selected to group_by let's say 'am' and 'gear' two selectInput's with the information of those variables will show on screen to create another table with filtered data depending on the selectInput's. The data is filtered in order, so if in 'am' the value is "0" the 'gear' selectInput will only show you "3" and "4" because there are the only choices available for 'gear' if you choose "0" in 'am' and the result is shown in another table below the grouped table.
Everything works fine, the selectInput's only show the variables according to the selected values and the filter also works, but when I try to select another option in the selectInput's that filter the data, it only shows briefly the result and then it goes back to the value set by default so I can not see the data for other options.
I have done my research and I assume that it has something to do with an event that is triggering twice and resets the value instantly, but I've been trying a lot of stuff and I can't get it to work.
The output looks something like this:
And here is the code:
library(shiny)
library(shinyWidgets)
library(dplyr)
library(DT)
data(mtcars)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(uiOutput('group_filter'),
uiOutput('cb_cyl'),
uiOutput('cb_vs'),
uiOutput('cb_am'),
uiOutput('cb_gear'),
uiOutput('cb_carb')),
mainPanel(
dataTableOutput('grouped_table'),
dataTableOutput('filtered_table')
),
))
server <- function(input, output, session) {
data<-mtcars
data$Amount_Money<-0
for (i in 1:length(data$mpg)) {
data[i,12]<-sample(50000:100000,1)
}
categories<-c('cyl','vs','am','gear','carb')
output$group_filter <- renderUI({
pickerInput("grouper", "Group by: ",
choices = levels(as.factor(categories)),
multiple = TRUE
)})
grouped_data<-
reactive ({
req(input$grouper)
data %>% group_by_at(input$grouper) %>% summarise(Money=sum(Amount_Money))
})
output$grouped_table<-renderDataTable(grouped_data())
filtered_data<-reactive({
req(input$grouper)
data<-grouped_data()
if (is.null(input$filter_cyl)) id_cyl <- "ALL" else id_cyl<-input$filter_cyl
if (is.null(input$filter_vs)) id_vs <- "ALL" else id_vs<-input$filter_vs
if (is.null(input$filter_am)) id_am <- "ALL" else id_am<-input$filter_am
if (is.null(input$filter_gear)) id_gear <- "ALL" else id_gear<-input$filter_gear
if (is.null(input$filter_carb)) id_carb <- "ALL" else id_carb<-input$filter_carb
available_filters<<-colnames(data)
number_filters<<-length(available_filters)-1 #NOT COUNTING AMOUNT MONEY COLUMN
for (i in 1:number_filters) { #THESE FOR GUARANTEES THAT THE DATA IS FILTER IN THE ORDER THAT THE USER SPECIFIED
if (available_filters[i]=="cyl") {
choices_cyl<-unique(data$cyl) #I NEED TO DEFINE CHOICES BEFORE THE FILTER SO THE USER CAN CHANGE TO THE OTHER CYL OPTIONS
data<-data %>%
filter(cyl==id_cyl) #NOW THAT THE DATA IS FILTERED BY THE VALUE THE USER SELECTED THE NEXT FILTERS WILL ONLY THE VALUE SELECTED HERE
}
if (available_filters[i]=="vs") {
choices_vs<-unique(data$vs)
data<-data %>%
filter(vs==id_vs)
}
if (available_filters[i]=="am") {
choices_am<-unique(data$am)
data<-data %>%
filter(am==id_am)
}
if (available_filters[i]=="gear") {
choices_gear<-unique(data$gear)
data<-data %>%
filter(gear==id_gear)
}
if (available_filters[i]=="carb") {
choices_carb<-unique(data$carb)
data<-data %>%
filter(carb==id_carb)
}
}
result<-list(1)
if (exists("choices_cyl")) result[[1]]<-choices_cyl
if (exists("choices_vs")) result[[2]]<-choices_vs
if (exists("choices_am")) result[[3]]<-choices_am
if (exists("choices_gear")) result[[4]]<-choices_gear
if (exists("choices_carb")) result[[5]]<-choices_carb
result[[6]]<-data
return(result)
})
output$filtered_table<-renderDataTable(filtered_data()[[6]])
output$cb_cyl <- renderUI({
base<-filtered_data()[[6]]
if ("cyl" %in% colnames(base)) {
selectInput(inputId = 'filter_cyl',
label ="cyl: ",
choices = sort(filtered_data()[[1]]), #sort(l)
selected = sort(filtered_data()[[1]])[1])
} else{
return(NULL)
}
})
output$cb_vs <- renderUI({
base<-filtered_data()[[6]]
if ("vs" %in% colnames(base)) {
selectInput(inputId = 'filter_vs',
label ="vs: ",
choices = sort(filtered_data()[[2]]), #sort(l)
selected = sort(filtered_data()[[2]])[1])
} else{
return(NULL)
}
})
output$cb_am <- renderUI({
base<-filtered_data()[[6]]
if ("am" %in% colnames(base)) {
selectInput(inputId = 'filter_am',
label ="am: ",
choices = sort(filtered_data()[[3]]), #sort(l)
selected = sort(filtered_data()[[3]])[1])
} else{
return(NULL)
}
})
output$cb_gear <- renderUI({
base<-filtered_data()[[6]]
if ("gear" %in% colnames(base)) {
selectInput(inputId = 'filter_gear',
label ="gear: ",
choices = sort(filtered_data()[[4]]), #sort(l)
selected = sort(filtered_data()[[4]])[1])
} else{
return(NULL)
}
})
output$cb_carb <- renderUI({
base<-filtered_data()[[6]]
if ("carb" %in% colnames(base)) {
selectInput(inputId = 'filter_carb',
label ="carb: ",
choices = sort(filtered_data()[[5]]), #sort(l)
selected = sort(filtered_data()[[5]])[1])
} else{
return(NULL)
}
})
}
shinyApp(ui, server)
Upvotes: 0
Views: 71
Reputation: 21349
You have multiple issues. There is no need to use for
loop. If you needed it, it is best to use local
inside it. You can google on how to use for
loops inside shiny
(lazy evaluation, etc.). Also, you are using filtered_data
inside renderUI for each selectInput
. However, you are using the input variables from those same selectInput
s to define the filtered_data
. It is best to switch it to grouped_data
in each renderUI
. Try this
library(shiny)
library(shinyWidgets)
library(dplyr)
library(DT)
data(mtcars)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(uiOutput('group_filter'),
uiOutput('cb_cyl'),
uiOutput('cb_vs'),
uiOutput('cb_am'),
uiOutput('cb_gear'),
uiOutput('cb_carb')
),
mainPanel(
DTOutput('grouped_table'),
DTOutput('filtered_table')
),
))
server <- function(input, output, session) {
data<-mtcars
data$Amount_Money<-0
for (i in 1:length(data$mpg)) {
data[i,12]<-sample(50000:100000,1)
}
categories<-c('cyl','vs','am','gear','carb')
output$group_filter <- renderUI({
pickerInput("grouper", "Group by: ",
choices = levels(as.factor(categories)),
multiple = TRUE
)})
grouped_data <- reactive({
req(input$grouper)
data %>% group_by_at(input$grouper) %>%
dplyr::summarise(Money=sum(Amount_Money)) %>% ungroup()
})
output$grouped_table<-renderDT(grouped_data())
filtered_data<-reactive({
req(input$grouper)
data<-grouped_data()
if (is.null(input$filter_cyl)) id_cyl <- "ALL" else id_cyl<-input$filter_cyl
if (is.null(input$filter_vs)) id_vs <- "ALL" else id_vs<-input$filter_vs
if (is.null(input$filter_am)) id_am <- "ALL" else id_am<-input$filter_am
if (is.null(input$filter_gear)) id_gear <- "ALL" else id_gear<-input$filter_gear
if (is.null(input$filter_carb)) id_carb <- "ALL" else id_carb<-input$filter_carb
available_filters <- colnames(data)
number_filters <- length(available_filters) #NOT COUNTING AMOUNT MONEY COLUMN
# for (i in 1:number_filters) { #THESE FOR GUARANTEES THAT THE DATA IS FILTER IN THE ORDER THAT THE USER SPECIFIED
# local({
# i <- i
if ("cyl" %in% available_filters) {
choices_cyl<-unique(data$cyl) #I NEED TO DEFINE CHOICES BEFORE THE FILTER SO THE USER CAN CHANGE TO THE OTHER CYL OPTIONS
data<-data %>%
dplyr::filter(cyl %in% input$filter_cyl) #NOW THAT THE DATA IS FILTERED BY THE VALUE THE USER SELECTED THE NEXT FILTERS WILL ONLY THE VALUE SELECTED HERE
}
if ("vs" %in% available_filters) {
choices_vs<-unique(data$vs)
data<-data %>%
dplyr::filter(vs %in% input$filter_vs)
}
if ("am" %in% available_filters) {
choices_am<-unique(data$am)
data<-data %>%
dplyr::filter(am %in% input$filter_am)
}
if ("gear" %in% available_filters) {
choices_gear<-unique(data$gear)
data<-data %>%
dplyr::filter(gear %in% input$filter_gear)
}
if ("carb" %in% available_filters) {
choices_carb<-unique(data$carb)
data<-data %>%
dplyr::filter(carb %in% input$filter_carb)
}
# })
# }
result<-list(1)
if (exists("choices_cyl")) result[[1]]<-choices_cyl
if (exists("choices_vs")) result[[2]]<-choices_vs
if (exists("choices_am")) result[[3]]<-choices_am
if (exists("choices_gear")) result[[4]]<-choices_gear
if (exists("choices_carb")) result[[5]]<-choices_carb
result[[6]]<-data
return(result)
})
output$filtered_table<-renderDT(datatable(filtered_data()[[6]]) )
output$cb_cyl <- renderUI({
base<- grouped_data() # filtered_data()[[6]]
if ("cyl" %in% colnames(base)) {
selectInput(inputId = 'filter_cyl',
label ="cyl: ",
choices = unique(base$cyl),
selected = base$cyl[1])
} else{
return(NULL)
}
})
output$cb_vs <- renderUI({
base<- grouped_data() #filtered_data()[[6]]
if ("vs" %in% colnames(base)) {
selectInput(inputId = 'filter_vs',
label ="vs: ",
choices = unique(base$vs),
selected = base$vs[1])
} else{
return(NULL)
}
})
output$cb_am <- renderUI({
base<- grouped_data() # filtered_data()[[6]]
if ("am" %in% colnames(base)) {
selectInput(inputId = 'filter_am',
label ="am: ",
choices = unique(base$am), #sort(l)
selected = base$am[1])
} else{
return(NULL)
}
})
output$cb_gear <- renderUI({
base<- grouped_data() # filtered_data()[[6]]
if ("gear" %in% colnames(base)) {
selectInput(inputId = 'filter_gear',
label ="gear: ",
choices = unique(base$gear), #sort(l)
selected = base$gear[1])
} else{
return(NULL)
}
})
output$cb_carb <- renderUI({
base<- grouped_data() # filtered_data()[[6]]
if ("carb" %in% colnames(base)) {
selectInput(inputId = 'filter_carb',
label ="carb: ",
choices = unique(base$carb), #sort(l)
selected = base$carb[1])
} else{
return(NULL)
}
})
}
shinyApp(ui, server)
Upvotes: 0