Related
When working with data all roads for me lead to "stratification tables" so one can get a feel for the dispersion of the data. Visualization is both by numeric table and plot.
Can someone please recommend a flexible way to generate a stratification table; by "flexible" I mean where the user can input stratification parameters? In the below code I present a sample data frame, and the ways I'd like the user to be eventually able to cut (stratify) the data.
I'm pretty new to R and have always run stratifications in Excel. In the image at the bottom you can see you how I normally stratify in Excel, with the end product highlighted in yellow. I also include a 2nd image that shows the formulas used to generate the stratification table in the first image.
I've been trying to limit the use of packages (other than shiny and the amazing dplyr, DT) but I imagine there are some nice packages too for running stratifications.
Note that my stratifications are run as of a specific point-in-time (in my data there 2 ways to measure time, via Period_1 and Period_2). So only those rows meeting that time criteria are included in the stratification.
Does anyone have suggestions for doing this?
Code:
library(shiny)
library(tidyverse)
library(shinyWidgets)
ui <-
fluidPage(
h5(strong("Raw data:")),
tableOutput("data"),
h5(strong("Grouped data:")),
radioButtons(
inputId = "grouping",
label = NULL,
choiceNames = c("By period 1", "By period 2"),
choiceValues = c("Period_1", "Period_2"),
selected = "Period_1",
inline = TRUE
),
tableOutput("summed_data"),
h5(strong("Point-in-time stratification table:")),
selectInput(inputId = "time",
label = "Choose a point-in-time:",
list(`By Period_1:` = list("2020-01", "2020-02", "2020-03", "2020-04"),
`By Period_2:` = list(1, 2, 3, 4)),
selected = "2020-04"),
numericInput(label = "Stratify by range of values:", 'strat_gap','',value=5,step=1,width = '100%'),
panel(
checkboxGroupInput(
inputId = "vars",
label = "Select characteristics to filter data by:",
choices = c("Category"),
selected = c("Category"),
inline = TRUE
),
selectizeGroupUI(
id = "my-filters",
params = list(
Category = list(inputId = "Category", title = "Category:")
)
),
status = "primary"
),
)
server <- function(input, output, session) {
data <- reactive({
data.frame(
ID = c(1,1,2,2,2,2,3,3,3),
Period_1 = c("2020-03", "2020-04", "2020-01", "2020-02", "2020-03", "2020-04", "2020-02", "2020-03", "2020-04"),
Period_2 = c(1, 2, 1, 2, 3, 4, 1, 2, 3),
Category = c("Toad", "Toad", "Stool", "Stool", "Stool", "Stool","Toad","Toad","Toad"),
Values = c(15, 25, 35, 45, 55, 87, 10, 20, 30)
)
})
choice <- reactive(input$grouping)
summed_data <- reactive({
data() %>%
group_by(across(choice())) %>%
select("Values") %>%
summarise(across(everything(), sum, na.rm = TRUE)) %>%
filter(across(1,.fns = ~ .x %>% negate(is.na)() ))
})
output$data <- renderTable(data())
output$summed_data <- renderTable(summed_data())
}
shinyApp(ui, server)
Excel example (2nd image shows stratification formulas):
In the interest of making this a more generalizable effort, here's how I would do it. In the UI, you can upload a CSV file and it grabs the names of the variables to use from the names in the file. There is one caveat here - the grouping variables have to have "Period" in their names somewhere. Otherwise, from there, you can choose the values to be summed from a list of the names of variables. The point in time values are taken from the observed values of the stratifying variable. You can also choose to filter on single variable and the values you can filter on are taken from the observed values of the filtering variable. Here's what it looks like:
and here is the code:
library(shiny)
library(tidyverse)
ui <-
fluidPage(
fluidRow(column(3, h5(strong("File Upload:"))),
column(3, h5(strong("Grouping:"))),
column(3, h5(strong("Point-in-time stratification table:"))),
column(3, h5(strong("Filtering:")))),
fluidRow(
column(3,
#actionButton("browser", "Browser"),
fileInput("file1", "Choose CSV File",
multiple = TRUE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",
".csv")),
tags$hr(),
# Input: Checkbox if file has header ----
checkboxInput("header", "Header", TRUE),
# Input: Select separator ----
radioButtons("sep", "Separator",
choices = c(Comma = ",",
Semicolon = ";",
Tab = "\t"),
selected = ","),
# Input: Select quotes ----
radioButtons("quote", "Quote",
choices = c(None = "",
"Double Quote" = '"',
"Single Quote" = "'"),
selected = '"')),
column(3,
uiOutput("values"),
uiOutput("period")),
column(3,
uiOutput("time"),
numericInput(label = "Stratify by range of values:", 'strat_gap','',value=5,step=1,width = '100%'),
),
column(3,
uiOutput("filter_var"),
uiOutput("filter_val")
)),
fluidRow(
column(6,
h5(strong("Raw data:")),
tableOutput("data"),
),
column(6,
h5(strong("Grouped data:")),
tableOutput("summed_data"),
)
)
)
server <- function(input, output, session) {
dat <- reactive({
req(input$file1)
read.csv(input$file1$datapath,
header = input$header,
sep = input$sep,
quote = input$quote)
})
output$period <- renderUI({
req(dat())
pds <- dat() %>% select(contains("Period")) %>% names
chc_pd <- pds
names(chc_pd) <- paste0("By ", gsub("_", "", pds))
selectInput(inputId = "period",
label = NULL,
choices = chc_pd,
selected = pds[1]
)
})
output$time <- renderUI({
req(dat())
req(input$period)
chc <- unique(na.omit(dat()[[input$period]]))
selectInput(inputId = "time",
label = "Choose a point-in-time:",
choices = chc,
selected = chc[1])
})
output$filter_var <- renderUI({
req(dat())
chc_filt <- names(dat())
selectizeInput("filter_var",
label = "Filtering Variable",
choices = c("", names(dat())),
selected="")
})
output$filter_val <- renderUI({
req(dat())
if(input$filter_var != ""){
chc_fv <- sort(unique(na.omit(dat()[[input$filter_var]])))
selectizeInput("filter_vals",
label="Filter Values",
choices = c("", chc_fv),
selected="",
multiple=TRUE)
}
})
output$values <- renderUI({
req(dat())
selectInput("vals",
"Variable to be Summarised",
choices = names(dat()),
selected = names(dat())[ncol(dat())])
})
output$data <- renderTable(dat())
output$summed_data <- renderTable({
breaks <- seq(min(dat()[[input$vals]], na.rm=TRUE),
max(dat()[[input$vals]], na.rm=TRUE),
by=input$strat_gap)
if(max(breaks) < max(dat()[[input$vals]], na.rm=TRUE)){
breaks <- c(breaks, max(breaks) + input$strat_gap)
}
qs <- ifelse(is.character(dat()[[input$period]]), "'", "")
filter_exp1 <- parse(text=paste0(input$period, "==", qs,input$time, qs))
tmp <- dat() %>%
filter(eval(filter_exp1))
if(input$filter_var != ""){
if(is.character(dat()[[input$filter_var]])){
fv <- paste("c(", paste("'", input$filter_vals, "'", collapse=",", sep=""), ")", sep="")
}else{
fv <- paste("c(", paste(input$filter_vals, collapse=",", sep=""), ")", sep="")
}
filter_exp2 <- parse(text=paste0(input$filter_var, "%in%", fv))
tmp <- tmp %>% filter(eval(filter_exp2))
}
tmp <- tmp %>%
mutate(sumvar = cut(!!sym(input$vals), breaks=breaks, include.lowest=TRUE)) %>%
group_by(sumvar) %>%
summarise(Count = n(),
Values = sum(!!sym(input$vals))) %>%
complete(sumvar, fill = list(Count = 0,
Values = 0)) %>%
ungroup %>%
mutate(Count_pct = sprintf("%.1f%%", (Count/sum(Count))*100),
Values_pct = sprintf("%.1f%%", (Values/sum(Values))*100)) %>%
dplyr::select(everything(), Count, Count_pct, Values, Values_pct)
names(tmp)[1] <- "Range"
tmp
})
# observeEvent(input$browser, {
# browser()
# })
}
shinyApp(ui, server)
I am constructing a shiny app. In the UI I have one selectInput and one pickerInput. Of course the pickerInput depends on the selectInput. In the example below, I want to find a way how to preserve what has been selected in pickerInput when users change the selectInput.
In the example below, let's imagine a user who selects Period 1: X to Z and either UK or USA or both UK and USA. What I want is that if that user changes Period 1: X to Z to Period 2: X to Y that UK be automatically selected -- or stay selected -- (because UK is among the choices of Period 2: X to Y).
So, how to retain what has been selected in pickerInput when input_period changes.
Thank you!
choice_name <- c('UK','USA','UK','USA','BE','BE')
choice_id <- c(1, 2, 1, 2, 3, 3)
period <- c('period1', 'period1', 'period2', 'period3', 'period3', 'period3')
data <- data.frame(choice_name, choice_id, period)
choices_picker <- unique(data$choice_id)
names(choices_picker) <- unique(data$choice_name)
ui <- bootstrapPage(
absolutePanel(left = 10, bottom = 10, draggable = TRUE,
selectInput(inputId = "input_period", label = "Period",
choices = c("Period 1: X to Z" = "period1", "Period 2: X to Y" = "period2", "Period 3: X to X" = "period3"),
selected = "period1"),
pickerInput(inputId = "picker_cty",
label = "Select Country",
choices = choices_picker,
multiple = TRUE),
))
server <- function(input, output, session) {
# Reactive pickerInput ---------------------------------
observeEvent(input$input_period, {
data1 <- data[data$period == input$input_period,]
datau <- unique(data$choice_id)
data1u <- unique(data1$choice_id)
disabled_choices <- ifelse(datau %in% data1u, 0,1)
# Generate reactive picker input
updatePickerInput(session = session,
inputId = "picker_cty",
choices = choices_picker,
choicesOpt = list(
disabled = disabled_choices,
style = ifelse(disabled_choices,
yes = "color: rgba(119, 119, 119, 0.5);",
no = "")
))
}, ignoreNULL=FALSE)
}
shinyApp(ui, server)
You can use select = option. Try this
choice_name <- c('UK','USA','UK','USA','BE','BE')
choice_id <- c(1, 2, 1, 2, 3, 3)
period <- c('period1', 'period1', 'period2', 'period3', 'period3', 'period3')
data <- data.frame(choice_name, choice_id, period)
data2 <- data[data$period == "period1",]
choices_picker <- unique(data$choice_id)
names(choices_picker) <- unique(data$choice_name)
datau <- unique(data$choice_id)
data2u <- unique(data2$choice_id)
disabled_choicez <- ifelse(datau %in% data2u, 0,1)
ui <- bootstrapPage(
absolutePanel(left = 10, bottom = 10, draggable = TRUE,
selectInput(inputId = "input_period", label = "Period",
choices = c("Period 1: X to Z" = "period1", "Period 2: X to Y" = "period2", "Period 3: X to X" = "period3"),
selected = "period1" ),
pickerInput(inputId = "picker_cty",
label = "Select Country",
choices = choices_picker,
choicesOpt = list(
disabled = disabled_choicez,
style = ifelse(disabled_choicez,
yes = "color: rgba(119, 119, 119, 0.5);",
no = "")
),
selected = character(0),
multiple = TRUE),
))
server <- function(input, output, session) {
observe({print(input$picker_cty)})
# Reactive pickerInput ---------------------------------
observeEvent(input$input_period, {
data1 <- data[data$period == input$input_period,]
datau <- unique(data$choice_id)
data1u <- unique(data1$choice_id)
disabled_choices <- ifelse(datau %in% data1u, 0,1)
if (is.null(input$picker_cty)) selected = character(0)
else {
if (sum(data1u %in% input$picker_cty)>0) {
selected = data1u[data1u %in% input$picker_cty]
}else selected = character(0)
}
# Generate reactive picker input
updatePickerInput(session = session,
inputId = "picker_cty",
choices = choices_picker,
selected = selected,
choicesOpt = list(
disabled = disabled_choices,
style = ifelse(disabled_choices,
yes = "color: rgba(119, 119, 119, 0.5);",
no = "")
))
}, ignoreInit = TRUE)
}
shinyApp(ui, server)
The following app generates a dynamic UI based on the number of variables selected. A problem is that when the number of variables selected is odd, the app generates an extra UI that is not tied to any of the variables previously selected. I've tried include if statements within the fluidRow creation statement, essentially checking if there is a remainder and if so, I've tried to tell the app to insert a blank space, but this doesn't do the trick. Does anyone have any suggestions on how to fix the issue?
## libraries
library(tidyverse)
library(shiny)
ui <- fluidPage(
selectInput(inputId = "var",
label = "vars:",
choices = colnames(mtcars),
multiple = TRUE),
uiOutput("dynUI")
)
server <- function(input, output, session) {
output$dynUI <- renderUI({
row_idx <- length(input$var) %>% seq_len
row_idx <- row_idx[row_idx %% 2 == 1]
row_idx %>%
map(~fluidRow(column(width = 2,
selectizeInput(inputId = paste0("var", .x),
label = paste(input$var[.x], "var:"),
choices = c("this", "that"),
multiple = FALSE)),
column(width = 2,
selectizeInput(inputId = paste0("var", .x + 1),
label = paste(input$var[.x + 1], "var:"),
choices = c("this", "that"),
multiple = FALSE))))
})
}
shinyApp(ui, server)
You can detect the odd variable using is.na(input$var[.x + 1]) then span it on 4 columns as in :
row_idx %>%
map( ~ {
if (!is.na(input$var[.x + 1]))
fluidRow(column(
width = 2,
selectizeInput(
inputId = paste0("var", .x),
label = paste(input$var[.x], "var:"),
choices = c("this", "that"),
multiple = FALSE
)
),
column(
width = 2,
selectizeInput(
inputId = paste0("var", .x + 1),
label = paste(input$var[.x + 1], "var:"),
choices = c("this", "that"),
multiple = FALSE
)
))
else
fluidRow(column(
width = 4,
selectizeInput(
inputId = paste0("var", .x),
label = paste(input$var[.x], "var:"),
choices = c("this", "that"),
multiple = FALSE
)
))
})
What about something like the following?
library(tidyverse)
library(shiny)
column2 = function(x, input) {
column(
width = 2,
selectizeInput(
inputId = paste0("var", x),
label = paste(input$var[x], "var:"),
choices = c("this", "that"),
multiple = FALSE
)
)
}
ui <- fluidPage(
selectInput(inputId = "var",
label = "vars:",
choices = colnames(mtcars),
multiple = TRUE),
uiOutput("dynUI")
)
server <- function(input, output, session) {
output$dynUI <- renderUI({
row_idx <- length(input$var) %>% seq_len
row_idx <- split(row_idx, (seq(row_idx) - 1) %/% 2)
map(row_idx, function(x, input) fluidRow(map(x, column2, input = input)), input = input)
})
}
shinyApp(ui, server)
EDIT:
This question is an extension of the question I posted: this question
I created a dataframe with 3 columns: num, id and val. I want my shiny app to do the following:
a dataframe dat is filtered by num column
select an value from id column from dat (selectInput).
add text comment in a text box (textInput)
click on an action button
A new column called comment is created in the data table, text comments are added to the comment column in the row where id equals the value selected.
The code is below. I cannot figure out why it's not working.
Thank a lot in advance!
library(shiny)
library(DT)
dat = data.frame(num=rep(1:2, each=5), id=rep(LETTERS[1:5],2), val=rnorm(10))
ui = fluidPage(
fluidRow(
column(12, selectInput('selectNum', label='Select Num',
choices=1:10, selected='')),
column(2, selectInput(inputId = 'selectID',
label = 'Select ID2',
choices = LETTERS[1:10],
selected='',
multiple=TRUE)),
column(6, textInput(inputId = 'comment',
label ='Please add comment in the text box:',
value = "", width = NULL,
placeholder = NULL)),
column(2, actionButton(inputId = "button",
label = "Add Comment"))
),
fluidRow (
column(12, DT::dataTableOutput('data') )
)
)
server <- function(input, output, session) {
## make df reactive
df = reactive ({ dat %>% filter(num %in% input$selectNum) })
df_current <- reactiveVal(df())
observeEvent(input$button, {
req(df_current())
## update df by adding comments
df_new <- df_current()
df_new[df_current()$id %in% input$selectID, "Comment"] <- input$comment
df_current(df_new)
})
output$data <- DT::renderDataTable({
req(df_current())
DT::datatable(df_current(),
options = list(orderClasses = TRUE,
lengthMenu = c(5, 10, 20), pageLength = 5))
})
shinyApp(ui=ui, server=server)
Instead of using a reactive/eventReactive statement for df, it might be more natural to keep track of previously inputted comments in the Comment column using a reactiveVal object for df. See also the responses to this question: R Shiny: reactiveValues vs reactive. If you prefer to use a reactive/eventReactive statement for df it is probably better to work with a separate object to store previous input comments (instead of incorporating it into the reactive statement for df).
library(shiny)
library(DT)
dat = data.frame(num=1:10, id=LETTERS[1:10], val=rnorm(10))
ui = fluidPage(
fluidRow(
column(12, selectInput('selectNum', label='Select Num',
choices=1:10)),
column(2, selectInput(inputId = 'selectID',
label = 'Select ID2',
choices = LETTERS[1:10],
selected='',
multiple=TRUE)),
column(6, textInput(inputId = 'comment',
label ='Please add comment in the text box:',
value = "", width = NULL,
placeholder = NULL)),
column(2, actionButton(inputId = "button",
label = "Add Comment"))
),
fluidRow (
column(12, DT::dataTableOutput('data') )
)
)
server <- function(input, output, session) {
## make df reactive
df_current <- reactiveVal(dat)
observeEvent(input$button, {
req(df_current(), input$selectID %in% dat$id)
## update df by adding comments
df_new <- df_current()
df_new[df_current()$id %in% input$selectID, "Comment"] <- input$comment
df_current(df_new)
})
output$data <- DT::renderDataTable({
req(df_current())
## filter df_current by 'selectNum'
df_filtered <- df_current()[df_current()$num %in% input$selectNum, ]
## show comments if non-empty
showComments <- is.null(df_filtered$Comment) || !all(is.na(df_filtered$Comment))
DT::datatable(df_filtered,
options = list(orderClasses = TRUE,
lengthMenu = c(5, 10, 20), pageLength = 5,
columnDefs = list(
list(targets = ncol(df_filtered), visible = showComments)
)
)
)
})
}
shinyApp(ui=ui, server=server)
Edit: below an edited server function that using df_current <- reactive({...}) instead of df_current <- reactiveVal({...}) and defining a separate reactiveVal object to keep track of the comments.
server <- function(input, output, session) {
## initialize separate reactive object for comments
df_comments <- reactiveVal({
data.frame(
id = character(0),
Comment = character(0),
stringsAsFactors = FALSE
)
})
## reactive object df
df_current <- reactive({
## reactivity that df depends on
## currently df = dat does not change
df <- dat
## merge with current comments
if(nrow(df_comments()) > 0)
df <- merge(df, df_comments(), by = "id", all.x = TRUE)
return(df)
})
observeEvent(input$button, {
req(input$selectID)
## update df_comments by adding comments
df_comments_new <- rbind(df_comments(),
data.frame(id = input$selectID, Comment = input$comment)
)
## if duplicated id's keep only most recent rows
df_comments_new <- df_comments_new[!duplicated(df_comments_new$id, fromLast = TRUE), , drop = FALSE]
df_comments(df_comments_new)
})
output$data <- DT::renderDataTable({
req(df_current())
## filter df_current by 'selectNum'
df_filtered <- df_current()[df_current()$num %in% input$selectNum, ]
## show comments if non-empty
showComments <- is.null(df_filtered$Comment) || !all(is.na(df_filtered$Comment))
DT::datatable(df_filtered,
options = list(orderClasses = TRUE,
lengthMenu = c(5, 10, 20), pageLength = 5,
columnDefs = list(
list(targets = ncol(df_filtered), visible = showComments)
)
)
)
})
}
There you have got a working example.
I think the thing is that you are trying to update a value through an observeEvent which is not good according to the documentation. ?observeEvent
Use observeEvent whenever you want to perform an action in response to an event. (Note that "recalculate a value" does not generally count as performing an action–see eventReactive for that.)
library(shiny)
library(DT)
dat = data.frame(num=1:10, id=LETTERS[1:10], val=rnorm(10))
ui = fluidPage(
fluidRow(
column(12, selectInput('selectNum', label='Select Num',
choices=1:10, selected='')),
column(2, selectInput(inputId = 'selectID',
label = 'Select ID2',
choices = LETTERS[1:10],
selected='',
multiple=TRUE)),
column(6, textInput(inputId = 'comment',
label ='Please add comment in the text box:',
value = "", width = NULL,
placeholder = NULL)),
column(2, actionButton(inputId = "button",
label = "Add Comment"))
),
fluidRow (
column(12, DT::dataTableOutput('data') )
)
)
server <- function(input, output, session) {
## make df reactive
df_current = reactive({
df = dat %>% filter(num %in% input$selectNum)
if(input$button != 0) {
input$button
df[df$id %in% input$selectID, "Comment"] <- isolate(input$comment)
}
return(df)
})
output$data <- DT::renderDataTable({
req(df_current())
DT::datatable(df_current(),
options = list(orderClasses = TRUE,
lengthMenu = c(5, 10, 20), pageLength = 5))
})
}
shinyApp(ui=ui, server=server)
So you can either go with your reactive value or using eventReactive as stated in the doc.
I am having trouble when initializing a shiny app in R. I would like eventReactive to trigger from any of several events, which are chained by reactive expressions. The app mostly works as intended, but does not display upon initialization and instead requires user to select an actionButton before results are displayed. Why is this?
I read documentation for eventReactive, played with ignoreNULL and ignoreInit settings, and done many online searches.
Example below.
require(shiny)
require(ggplot2)
ui <- fluidPage(
titlePanel("Car Weight"),
br(),
uiOutput(outputId = "cylinders"),
sidebarLayout(
mainPanel(
# plotOutput(outputId = "trend"),
# plotOutput(outputId = "hist"),
tableOutput("table"),
uiOutput(outputId = "dataFilter"),
actionButton(inputId = "update1", label = "Apply Filters"),
width = 9
),
sidebarPanel(
actionButton(inputId = "update2", label = "Apply Filters"),
uiOutput(outputId = "modelFilter"),
actionButton(inputId = "update3", label = "Apply Filters"),
width = 3
)
)
)
server <- function(input, output) {
# Read data. Real code will pull from database.
df <- mtcars
df$model <- row.names(df)
# Get cylinders
output$cylinders <- renderUI(
selectInput(
inputId = "cyl",
label = "Select Cylinders",
choices = c("", as.character(unique(df$cyl)))
)
)
# Subset data by cyl.
df2 <-
reactive(droplevels(df[df$cyl == input$cyl, ]))
# Filter data.
df3 <-
eventReactive({
##############################################################
# Help needed:
# Why does this block not update upon change in 'input$cyl'?
##############################################################
input$update1
input$update2
input$update3
input$cyl
},
{
req(input$modelFilter)
modelFilterDf <-
data.frame(model = input$modelFilter)
df3a <-
merge(df2(), modelFilterDf, by = "model")
df3a[df3a$wt >= input$dataFilter[1] &
df3a$wt <= input$dataFilter[2],]
},
ignoreNULL = FALSE,
ignoreInit = FALSE)
# Plot table.
output$table <- renderTable(df3())
# Filter by data value.
output$dataFilter <-
renderUI({
req(df2()$wt[1])
sliderInput(
inputId = "dataFilter",
label = "Filter by Weight (1000 lbs)",
min = floor(min(df2()$wt, na.rm = TRUE)),
max = ceiling(max(df2()$wt, na.rm = TRUE)),
value = c(
min(df2()$wt, na.rm = TRUE),
max(df2()$wt, na.rm = TRUE)
),
step = round(
max(df2()$wt, na.rm = TRUE) - min(df2()$wt, na.rm = TRUE)
) / 100,
round = round(log((
max(df2()$wt, na.rm = TRUE) - min(df2()$wt, na.rm = TRUE)
) / 100))
)
})
# Filter by lot / wafer.
output$modelFilter <- renderUI({
req(input$cyl)
checkboxGroupInput(
inputId = "modelFilter",
label = "Filter by Model",
choices = as.character(unique(df2()$model)),
selected = as.character(unique(df2()$model))
)
})
}
# Run shiny.
shinyApp(ui = ui, server = server)
I found a solution. Perhaps not the most elegant, but it works.
The problem was that input$modelFilter and input$modelFilter were one update behind df2. This did not matter when the user selected input$update, since df2 did not update, and only posed a problem during a newly created df2, since the filter would not match the data.
To resolve this, I added values <- reactiveValues(update = 0) which will increase by +1 every time df3 is created, and will reset back to 0 when a new df2 is created. If values$update > 0 then the data is filtered, otherwise, the unfiltered data is returned.
Possibly useful link: How can I set up triggers or execution order for eventReactive or ObserveEvent?
require(shiny)
require(ggplot2)
ui <- fluidPage(
titlePanel("Car Weight"),
br(),
uiOutput(outputId = "cylinders"),
sidebarLayout(
mainPanel(
tableOutput("table"),
uiOutput(outputId = "dataFilter"),
actionButton(inputId = "update1", label = "Apply Filters"),
width = 9
),
sidebarPanel(
actionButton(inputId = "update2", label = "Apply Filters"),
uiOutput(outputId = "modelFilter"),
actionButton(inputId = "update3", label = "Apply Filters"),
width = 3
)
)
)
server <- function(input, output) {
# Read data. Real code will pull from database.
df <- mtcars
df$model <- row.names(df)
df <- df[order(df$model), c(12,1,2,3,4,5,6,7,8,9,10,11)]
# Get cylinders
output$cylinders <- renderUI({
selectInput(
inputId = "cyl",
label = "Select Cylinders",
choices = c("", as.character(unique(df$cyl)))
)})
# Check if data frame has been updated.
values <- reactiveValues(update = 0)
# Subset data by cyl.
df2 <-
reactive({
values$update <- 0
df2 <- droplevels(df[df$cyl == input$cyl,])})
# Filter data.
df3 <-
eventReactive({
input$update1
input$update2
input$update3
df2()
},
{
if (values$update > 0) {
req(input$modelFilter)
modelFilterDf <-
data.frame(model = input$modelFilter)
df3a <-
merge(df2(), modelFilterDf, by = "model")
df3a <- df3a[df3a$wt >= input$dataFilter[1] &
df3a$wt <= input$dataFilter[2], ]
} else {
df3a <- df2()
}
values$update <- values$update + 1
df3a
},
ignoreNULL = FALSE,
ignoreInit = TRUE)
# Plot table.
output$table <- renderTable(df3())
# Filter by data value.
output$dataFilter <-
renderUI({
req(df2()$wt[1])
sliderInput(
inputId = "dataFilter",
label = "Filter by Weight (1000 lbs)",
min = floor(min(df2()$wt, na.rm = TRUE)),
max = ceiling(max(df2()$wt, na.rm = TRUE)),
value = c(floor(min(df2()$wt, na.rm = TRUE)),
ceiling(max(df2()$wt, na.rm = TRUE))),
step = round(max(df2()$wt, na.rm = TRUE) - min(df2()$wt, na.rm = TRUE)) / 100,
round = round(log((
max(df2()$wt, na.rm = TRUE) - min(df2()$wt, na.rm = TRUE)
) / 100))
)
})
# Filter by lot / wafer.
output$modelFilter <- renderUI({
req(input$cyl)
checkboxGroupInput(
inputId = "modelFilter",
label = "Filter by Model",
choices = as.character(unique(df2()$model)),
selected = as.character(unique(df2()$model))
)
})
}
# Run shiny.
shinyApp(ui = ui, server = server)