Using fluidRow with a conditional statement - r

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:

Related

How to create a flexible data stratification table?

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)

Create plotly grouped bar chart with dynamic number of groups in a shiny app

I have the shiny app below in which the user is able to select one or both dataframes and then based on the other filters to create a bar plot which will display the values of one or both dataframes. Now I have not connected it to the widgets in order to display what I want to achieve. It confuses me how to give the dataframes df1 and df2 to the plot_ly() and add_trace() functions together or one at a time.
library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
library(shinyWidgets)
library(dplyr)
library(plotly)
shinyApp(
ui = dashboardPagePlus(
header = dashboardHeaderPlus(title = "Social Media Metrics", titleWidth = 320
),
sidebar = dashboardSidebar(width = 320,
checkboxGroupInput("checkGroup", label = "Select Dataset",
choices = list("df1", "df2"),
selected = "df1"),
checkboxGroupInput("checkGroup2", label = "Select Social Network",
choices = list("FACEBOOK", "INSTAGRAM"),
selected = "FACEBOOK"),
radioButtons("radio", label = "Choose type of values",
choices = list("Absolute", "Percentages" ),
selected = "Absolute"),
uiOutput("x30"),
uiOutput("x16"),
uiOutput("value")
),
body = dashboardBody(
plotlyOutput("plot")
)
),
server = function(input, output) {
page<-c("ONE","TWO","THREE")
network<-c("INSTAGRAM","FACEBOOK","FACEBOOK")
av<-c(3.5,7.2,8.7)
growth<-c(5,7,9)
av2<-c(3.5,7.2,8.7)
growth2<-c(5,7,9)
df1<-data.frame(page,network,av,growth,av2,growth2)
page<-c("ONE","TWO","THREE")
network<-c("INSTAGRAM","FACEBOOK","FACEBOOK")
av<-c(4.5,7.9,8.7)
growth<-c(5,6,9)
av2<-c(3.5,9.2,8.7)
growth2<-c(5,43,9)
df2<-data.frame(page,network,av,growth,av2,growth2)
output$x30<-renderUI({
if("df1" %in% input$checkGroup){
new<-subset(df1, network %in% input$checkGroup2)
pickerInput(
inputId = "x3"#The colname of selected column
,
label = "Select Profile-df1" #The colname of selected column
,
choices = as.character(new$page)#all rows of selected column
,
multiple = TRUE,options = list(`actions-box` = TRUE)
)
}
else{
return(NULL)
}
})
output$x16<-renderUI({
if("df2" %in% input$checkGroup){
new<-subset(df2, network %in% input$checkGroup2)
pickerInput(
inputId = "x6"#The colname of selected column
,
label = "Select Profile-df2" #The colname of selected column
,
choices = as.character(new$page)#all rows of selected column
,
multiple = TRUE,options = list(`actions-box` = TRUE)
)
}
else{
return(NULL)
}
})
output$value<-renderUI({
if(input$radio=="Absolute"){
pickerInput(
inputId = "val"
,
label = "Select Absolut Value"
,
choices = c("growth","growth2")#all rows of selected column
,
multiple = F,options = list(`actions-box` = TRUE)
)
}
else{
pickerInput(
inputId = "val"
,
label = "Select Percentage Value"
,
choices = c("av","av2")#all rows of selected column
,
multiple = F,options = list(`actions-box` = TRUE)
)
}
})
output$plot<-renderPlotly({
fig <- plot_ly(df1, x = ~page, y = ~growth, type = 'bar', name = 'growth')
fig <- fig %>% add_trace(df2,y = ~growth, name = 'growth')
fig <- fig %>% layout(yaxis = list(title = 'Count'), barmode = 'group')
fig
})
}
)
A reactive dataframe dfa() gives the selected data from either or both dataframes. Now, plotly requires some work. Please note that working with variables within dfa() gives some errors as both df1 and df2 are defined within the server and are available in output$plot, and hence, you may need to use dfa()$id, etc.
library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
library(shinyWidgets)
library(dplyr)
library(plotly)
shinyApp(
ui = dashboardPagePlus(
header = dashboardHeaderPlus(title = "Social Media Metrics", titleWidth = 320
),
sidebar = dashboardSidebar(width = 320,
checkboxGroupInput("checkGroup", label = "Select Dataset",
choices = list("df1", "df2"),
selected = "df1"),
checkboxGroupInput("checkGroup2", label = "Select Social Network",
choices = list("FACEBOOK", "INSTAGRAM"),
selected = "FACEBOOK"),
radioButtons("radio", label = "Choose type of values",
choices = list("Absolute", "Percentages" ),
selected = "Absolute"),
uiOutput("x30"),
uiOutput("x16"),
uiOutput("value")
),
body = dashboardBody(
plotlyOutput("plot") , DTOutput("tb1")
)
),
server = function(input, output) {
page<-c("ONE","TWO","THREE")
network<-c("INSTAGRAM","FACEBOOK","FACEBOOK")
av<-c(3.5,4.2,8.7)
growth<-c(4,7,9)
av2<-c(3.5,7.2,4.7)
growth2<-c(4,7,9)
id <- rep("df1",3)
df1<-data.frame(page,network,av,growth,av2,growth2,id)
page<-c("ONE","TWO","THREE")
network<-c("INSTAGRAM","FACEBOOK","FACEBOOK")
av<-c(4.5,7.9,8.7)
growth<-c(5,4,8)
av2<-c(3.5,9.2,6.7)
growth2<-c(5,4,9)
id <- rep("df2",3)
df2<-data.frame(page,network,av,growth,av2,growth2,id)
output$x30<-renderUI({
if (is.null(input$checkGroup)) {
return(NULL)
}else if("df1" %in% input$checkGroup){
new<-subset(df1, network %in% input$checkGroup2)
pickerInput(
inputId = "x3" #The rownames of selected column
,
label = "Select Profile-df1" #The colname of selected column
,
choices = as.character(new$page) #all rows of selected column
,
multiple = TRUE,options = list(`actions-box` = TRUE)
)
}
else{
return(NULL)
}
})
output$x16<-renderUI({
if (is.null(input$checkGroup)) {
return(NULL)
}else if( "df2" %in% input$checkGroup){
new2<-subset(df2, network %in% input$checkGroup2)
pickerInput(
inputId = "x6" #The rownames of selected column
,
label = "Select Profile-df2" #The colname of selected column
,
choices = as.character(new2$page) #all rows of selected column
,
multiple = TRUE,options = list(`actions-box` = TRUE)
)
}
else{
return(NULL)
}
})
output$value<-renderUI({
if(req(input$radio)=="Absolute"){
pickerInput(
inputId = "val"
,
label = "Select Absolut Value"
,
choices = c("growth","growth2") #all rows of selected column
,
multiple = F, options = list(`actions-box` = TRUE)
)
}else if(req(input$radio)=="Percentages"){
pickerInput(
inputId = "val"
,
label = "Select Percentage Value"
,
choices = c("av","av2")#all rows of selected column
,
multiple = F,options = list(`actions-box` = TRUE)
)
} else {return(NUL)}
})
dfa <- reactive({
req(input$val)
if (is.null(input$checkGroup)) {return(NULL)}
if (input$checkGroup == "df1" | length(input$checkGroup) == 2){
df11 <- df1 %>% filter(page %in% req(input$x3) & network %in% req(input$checkGroup2))
}else df11 <- NULL
if (input$checkGroup == "df2" | length(input$checkGroup) == 2){
df22 <- df2 %>% filter(page %in% req(input$x6) & network %in% req(input$checkGroup2))
}else df22 <- NULL
if (is.null(df11) & is.null(df22)) {return(NULL)
}else {
if (is.null(df11)){df <- df22
}else if (is.null(df22)){df <- df11
}else { df <- rbind(df11,df22) }
df <- df %>% transform(y=df[[as.name(input$val)]], x=page)
}
df
})
output$tb1 <- renderDT(dfa())
output$plot<-renderPlotly({
if (is.null(dfa())) return(NULL)
xvar <- unique(dfa()$page)
xform <- list(categoryorder = "array",
categoryarray = xvar) ## to get the bars in the order you wish to display
fig <- plot_ly()
fig <- fig %>% add_trace( dfa() , x = ~x, y = ~y, type='bar', name='growth', fill=x, color=dfa()$id)
fig <- fig %>% layout(xaxis = list(xform, title="Page"),
yaxis = list(title = 'Count'), barmode = 'group')
fig
})
}
)

How to implement eventReactive with multiple reactive eventExpr?

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)

Challenge while updating multiple inputs in shiny

UpdateSliderInput not working...
Hi All,
Seems like a challenge updating sliderInput. So i wanted to develop an application in a way so that filter can be applied dynamically wherein one of the variables needs to be provided with a slider.
Any help can be really appriciable.
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fluidRow(
column(6, actionButton('addFilter', 'Add filter')),
offset = 6
),
tags$hr(),
tags$div(id = 'placeholderAddRemFilt'),
tags$div(id = 'placeholderFilter'),
tags$div(id = 'placeholderFilter')
# width = 4 # sidebar
),
mainPanel(
tableOutput("data")
)
)
)
server <- function(input, output,session) {
filter <- character(0)
makeReactiveBinding("aggregFilterObserver")
aggregFilterObserver <- list()
observeEvent(input$addFilter, {
add <- input$addFilter
filterId <- paste0('Filter_', add)
colfilterId <- paste0('Col_Filter_', add)
rowfilterId <- paste0('Row_Filter_', add)
removeFilterId <- paste0('Remove_Filter_', add)
headers <- names(mtcars)
insertUI(
selector = '#placeholderFilter',
# ui = tags$div(id = filterId,
# actionButton(removeFilterId, label = "Remove filter", style = "float: right;"),
# selectInput(colfilterId, label = "Some Filter", choices = as.list(headers), selected = 1),
# sliderInput(rowfilterId, label = "Select variable values",
# min = 1, max = 2, value = 1:4)
# )
ui = tags$div(column(9,id = filterId,
actionButton(removeFilterId, label = "Remove filter", style = "float: right;"),
selectInput(colfilterId, label = "Some Filter", choices = headers, selected = NULL),
conditionalPanel(condition = paste0("input.",colfilterId," != 'mpg'"),
checkboxGroupInput(rowfilterId, label = "Select variable values",
choices = NULL, selected = NULL, width = 4000)),
conditionalPanel(condition = paste0("input.",colfilterId," == 'mpg'"),
sliderInput(rowfilterId,
label = 'select values',
min = 1,#min(datafile$Age),
max = 10,#max(datafile$Age),
value = 1:5))#c(min(datafile$Age),max(datafile$Age))))
)
)
)
observeEvent(input[[colfilterId]], {
col <- input[[colfilterId]]
values <- as.list(unique(mtcars[col]))[[1]]
print(values)
print(paste0("example",as.list(unique(mtcars[col]))))
#
# updateCheckboxGroupInput(session, rowfilterId , label = "Select variable values",
# choices = values, selected = values, inline = TRUE)
#
updateSliderInput(session, rowfilterId , min = min(values), max = max(values), value = c(min(values),max(values)))
updateCheckboxGroupInput(session, rowfilterId , label = "Select variable values",
choices = values, selected = values, inline = TRUE)
aggregFilterObserver[[filterId]]$col <<- col
aggregFilterObserver[[filterId]]$rows <<- NULL
})
observeEvent(input[[rowfilterId]], {
rows <- input[[rowfilterId]]
aggregFilterObserver[[filterId]]$rows <<- rows
})
observeEvent(input[[removeFilterId]], {
removeUI(selector = paste0('#', filterId))
aggregFilterObserver[[filterId]] <<- NULL
})
})
output$data <- renderTable({
dataSet <- mtcars
invisible(lapply(aggregFilterObserver, function(filter){
dataSet <<- dataSet[which((dataSet[[filter$col]] %in% filter$rows)), ]
}))
dataSet
})
}
shinyApp(ui = ui, server = server)
Mpg values are not being updated, Is this due to conditionalPanel because of which the sliderInput is not being updated?
Everything seems to be perfect apart from the inputid you are using for 2 input types.
I just created one more variable for Sliderinput which will create dynamic input id.
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
fluidRow(
column(6, actionButton('addFilter', 'Add filter')),
offset = 6
),
tags$hr(),
tags$div(id = 'placeholderAddRemFilt'),
tags$div(id = 'placeholderFilter'),
width = 4 # sidebar
),
mainPanel(
tableOutput("data")
)
)
)
server <- function(input, output,session) {
filter <- character(0)
makeReactiveBinding("aggregFilterObserver")
aggregFilterObserver <- list()
observeEvent(input$addFilter, {
add <- input$addFilter
filterId <- paste0('Filter_', add)
colfilterId <- paste0('Col_Filter_', add)
rowfilterId <- paste0('Row_Filter_', add)
rowfilterId_num <- paste0('Row_Filter_num_', add)
removeFilterId <- paste0('Remove_Filter_', add)
headers <- names(mtcars)
insertUI(
selector = '#placeholderFilter',
ui = tags$div(id = filterId,
actionButton(removeFilterId, label = "Remove filter", style = "float: right;"),
selectInput(colfilterId, label = "Some Filter", choices = as.list(headers), selected = 1),
sliderInput(rowfilterId_num, label = "Select variable values",
min = 1, max = 2, value = 1:4)
)
)
observeEvent(input[[colfilterId]], {
print(rowfilterId)
print(paste0(input[[colfilterId]]))
col <- input[[colfilterId]]
values <- as.list(unique(mtcars[col]))[[1]]
print(values)
print(paste0("example",as.list(unique(mtcars[col]))))
updateCheckboxGroupInput(session, rowfilterId , label = "Select variable values",
choices = values, selected = values, inline = TRUE)
updateSliderInput(session, rowfilterId_num , label = "Select variable",min = min(values), max = max(values), value = c(min(values),max(values)))
aggregFilterObserver[[filterId]]$col <<- col
aggregFilterObserver[[filterId]]$rows <<- NULL
})
observeEvent(input[[rowfilterId]], {
rows <- input[[rowfilterId]]
aggregFilterObserver[[filterId]]$rows <<- rows
})
observeEvent(input[[removeFilterId]], {
removeUI(selector = paste0('#', filterId))
aggregFilterObserver[[filterId]] <<- NULL
})
})
output$data <- renderTable({
dataSet <- mtcars
invisible(lapply(aggregFilterObserver, function(filter){
dataSet <<- dataSet[which((dataSet[[filter$col]] %in% filter$rows)), ]
}))
dataSet
})
}
shinyApp(ui = ui, server = server)
just check and let me know that this is what you wanted to achieve. let me know incase any thing else is required.

How to select rows of a matrix which has to meet mutiple conditions in R Shiny

The goal is to build an application able to select and present only rows of a matrix that meets specific conditions selected by the user via Shiny elements such as checkboxes and sliderInput
Our data is subject to two (or more) ways to be filtered:
Via checkboxGroupInput where user can select one or more numbers
Via sliders. There will be one slider for each column of data. This allows user to select the range of numbers for each column.
I got stuck on making the data react to the selection entered by the user. Any suggestion is appreciated!
Here is the code that I have:
server.R
# Load libraries.
library(shiny)
library(datasets)
library(xtable)
library(R.utils)
shinyServer(
function(input, output) {
source('global.R', local=TRUE)
getDataName <- reactive({
out <- input$dataName
print(out)
return(out)
})
getData <- reactive({
cat("Getting data for, ", getDataName(), ".", sep = '')
if(getDataName() == ""){
print("ERROR: getDAtaName is empty! Check your code!")
out <- NULL
}
else {
dataSet <- t(combn(max(selectRange(getDataName())), numCols(getDataName())))
}
print(head(dataSet, n = 10))
return(dataSet)
})
selectedValues <- reactive({
print("Numbers selected via checkboxes:")
print(input$numSelector)
})
output$numSelector <- renderUI({
out <- checkboxGroupInput(
inputId = "numSelector",
label = "Select the numbers to be included in the rows",
choices = selectRange(input$dataName),
inline = TRUE
)
return(out)
})
output$sliders <- renderUI({
numSliders <- numCols(input$dataName)
lapply(1:numSliders, function(i) {
sliderInput(
inputId = paste0('column', i),
label = paste0('Select the range for column ', i),
min = min(selectRange(input$dataName)),
max = max(selectRange(input$dataName)),
value = c(min(selectRange(input$dataName)), max(selectRange(input$dataName))),
step =1)
})
})
output$selectedDataDisplay <- renderDataTable({
as.table(getData())}, options = list(lengthMenu = c(5, 30, 50), pageLength = 10))
}
)
ui.R
library(shiny)
shinyUI(
pageWithSidebar(
headerPanel("Selection zone"),
# Select inputs
sidebarPanel(
selectInput(
inputId = "dataName",
label = "Select data",
choices = c("data1", "data2", "data3", "data4")
),
uiOutput(outputId = "numSelector"),
uiOutput(outputId = "sliders")
),
mainPanel(
tableOutput("selectedDataDisplay"))
)
)
global.R
selectRange <- function(x){
if(x == "data1"){choices = c(1:10)}
if(x == "data2"){choices = c(1:15)}
if(x == "data3"){choices = c(1:20)}
if(x == "data4"){choices = c(1:25)}
return(choices)
}
numCols <- function(x){
if(x == "data1"){maxNum = 10
numCol = 5}
if(x == "data2"){maxNum = 15
numCol = 5}
if(x == "data3"){maxNum = 20
numCol = 5}
if(x == "data4"){maxNum = 25
numCol = 6}
return(numCol)
}
You did not provide your actual data sets, so I simulated a couple, and I don't have your exact formulas but hopefully you can extend the idea:
ui.R
shinyUI(
pageWithSidebar(
headerPanel("Selection zone"),
# Select inputs
sidebarPanel(
# User enters name of dat.frame here.
selectInput(
inputId = "dataName",
label = "Select your data",
choices = c("data1", "data2", "data3", "data4")
),
uiOutput(outputId = "numSelector"),
uiOutput(outputId = "sliders")
),
mainPanel(
tabsetPanel(
tabPanel("Model Summary", dataTableOutput("selectedDataDisplay"), textOutput("vars"))
)
)
))
server.R
library(shiny)
library(data.table)
data1 <- matrix(round(runif(10000, min = 1, max = 20), 0), ncol = 10)
data2 <- matrix(round(runif(10000, min = 1, max = 20), 0), ncol = 10)
data3 <- matrix(round(runif(10000, min = 1, max = 20), 0), ncol = 10)
data4 <- matrix(round(runif(10000, min = 1, max = 20), 0), ncol = 10)
shinyServer(function(input, output) {
output$numSelector <- renderUI({
out <- checkboxGroupInput(
inputId = "numSelector",
label = "Select the numbers to be included in the rows",
choices = 1:20,
inline = TRUE
)
return(out)
})
output$sliders <- renderUI({
numSliders <- eval(parse(text = c("ncol(",input$dataName, ")")))
lapply(1:numSliders, function(i) {
sliderInput(
inputId = paste0('column', i),
label = paste0('Select the range for column ', i),
min = 1,
max = 20,
value = c(1, 20),
step = 1)
})
})
dataSet <- reactive({
if ( is.null(input$column1) ){
} else {
colName <- "Column"
eval(parse(text = c(paste0("set <- as.data.table(", input$dataName, ")"))))
setnames(set, colnames(set), paste0(colName, seq(ncol(set))))
# generate boolean values for each column's rows based upon individual ranges & the over all
validRows <- list()
for(k in seq(ncol(set))){
validRows[[k]] <- eval(parse(text = paste0("with(set, ", colName, k, " %in% input$column", k, "[1]:input$column", k, "[2] & ", colName, k, " %in% input$numSelector )")))
}
validRows <- do.call(cbind, validRows)
# if any of the column's conditions are satisfied, the row is accepted
validRows <- apply(validRows, 1, any)
# ouput accepted rows
set[ validRows ]
}
})
output$selectedDataDisplay <- renderDataTable(dataSet(), options = list(lengthMenu = c(5, 30, 50), pageLength = 10))
})

Resources