Overview
Hello, I am trying to work with displaying different plots using checkboxes within tabsetPanels. I am working with a dynamic amount number of panels, so that is the reason for creating the UI contents within the server portion.
Ideal Output
For each tabPanel:
iris plot outputs if no checkboxes are selected
mtcars plot outputs if Box One is selected
islands plot outputs if Box Two is selected
sleep plot outputs if both Box One and Box Two are selected
What I have tried
-I have tried to use condtionalPanels to try to & capture the cases. I was under the impression that the value returns 'TRUE' if checked & 'FALSE' if unchecked, however I receive NULL for each of the boxed values, even if I set the default value to be checked.
-I believe my underlying issue is my lack of ability to trigger the dynamic checkboxes for each tabPanel
Disclaimer
This is a reproducible example, the default values of originally created tabPanels is set to 5. I did not accommodate proper code if the value were to change for the sake of simplicity.
Sample Code:
ui <- navbarPage(title="Dynamic tabsetPanels",id="navbar",
tabPanel("Home",
textInput(inputId = "numPanels",
label = "Enter # of Panels to produce",
value = 5)
),tabPanel("Analysis",
tabsetPanel(id = "tabs"))
)
server <- function(input, output) {
plotOne = renderPlot({plot(iris)})
plotTwo = renderPlot({plot(mtcars)})
plotThree = renderPlot({plot(islands)})
plotFour = renderPlot({plot(sleep)})
observe({
req(input$numPanels)
lapply(1:input$numPanels,function(i){
tabName = paste("Tab",i,sep=" ")
first = paste0("first",i)
second = paste0("second",i)
appendTab(inputId = "tabs",
tab = tabPanel(
tabName,
fluidPage(
sidebarLayout(
sidebarPanel(
#side-panel code
h2("Features"),
checkboxInput(inputId=first,label="Box One"),
checkboxInput(inputId=second,label="Box Two")
),mainPanel(
#output when nothing clicked
conditionalPanel(
condition = "!glue(input.{first} && !glue(input.{second})",
plotOutput(iris)
),
#output when box one is clicked
conditionalPanel(
condition = "glue(input.{first})",
plotOutput(mtcars)
),
#output when box two is clicked
conditionalPanel(
condition = "glue(input.{second})",
plotOutput(islands)
),
#output when box one and two are clicked
conditionalPanel(
condition = "glue(input.{first}) && glue(input.{second})",
plotOutput(sleep)
)
)
)
)
)
)
})
})
}
shinyApp(ui=ui, server=server)
Any suggestions would be greatly appreciated!
First issue with your code is the use of glue to create your conditions, i.e. you have to do e.g. condition = glue("input.{first}") instead of condition = "glue(input.{first})" to evaluate the glue string. Second issue is that in the plotOutputs you have to use the names of the outputs, e.g. plotOutput("plotOne") instead of plotOutput(iris). Finally, even after fixing these issues your app will not work as desired as you can't use outputs with the same id in several places or tabs, i.e. you get a duplicated id error. To fix that you also have to create a dynamic list of outputs so that the ids are unique.
library(shiny)
library(glue)
ui <- navbarPage(
title = "Dynamic tabsetPanels", id = "navbar",
tabPanel(
"Home",
textInput(
inputId = "numPanels",
label = "Enter # of Panels to produce",
value = 5
)
), tabPanel(
"Analysis",
tabsetPanel(id = "tabs")
)
)
server <- function(input, output) {
observe({
req(input$numPanels)
lapply(1:input$numPanels, function(i) {
output[[paste0("plotOne", i)]] <- renderPlot(plot(iris))
output[[paste0("plotTwo", i)]] <- renderPlot(plot(mtcars))
output[[paste0("plotThree", i)]] <- renderPlot(plot(islands))
output[[paste0("plotFour", i)]] <- renderPlot(plot(sleep))
})
})
observe({
req(input$numPanels)
lapply(1:input$numPanels, function(i) {
tabName <- paste("Tab", i, sep = " ")
first <- paste0("first", i)
second <- paste0("second", i)
appendTab(
inputId = "tabs",
tab = tabPanel(
tabName,
fluidPage(
sidebarLayout(
sidebarPanel(
# side-panel code
h2("Features"),
checkboxInput(inputId = first, label = "Box One"),
checkboxInput(inputId = second, label = "Box Two")
), mainPanel(
# output when nothing clicked
conditionalPanel(
condition = glue("!input.{first} && !input.{second}"),
plotOutput(paste0("plotOne", i))
),
# output when box one is clicked
conditionalPanel(
condition = glue("input.{first}"),
plotOutput(paste0("plotTwo", i))
),
# output when box two is clicked
conditionalPanel(
condition = glue("input.{second}"),
plotOutput(paste0("plotThree", i))
),
# output when box one and two are clicked
conditionalPanel(
condition = glue("input.{first} && input.{second}"),
plotOutput(paste0("plotFour", i))
)
)
)
)
)
)
})
})
}
shinyApp(ui = ui, server = server)
I have got a dashboard that pulls data from Google analytics or a CSV upload and then calculate conversion rate and average order value(for AB testing purpose).
I have been trying to implement filters that allow selecting device category e.g (mobile, tablet or desktop) and product category e.g(card, gift or flowers). The filters should pulled from from the data frame dynamically and then be available for selection in the drop downs.
I have seen a lot of similar example of this forum but for the life of me I haven't been able to make it work. The cases I have seen seem to be using observe ({}) but my issue seem to be coming from the fact that I need to pass the choices out of the reactive function first.
Below is a reproducible, simplified example, with data frame generated as they would appear.
I have commented out #choices= Results()$Devices in the UI so to show you how it looks like before it breaks.
Many thanks in advance
G
require(shiny)
require(shinydashboard)
require(googleVis)
require(dplyr)
ui <- dashboardPage(
skin="blue",
dashboardHeader(
title="Dashboard",
titleWidth = 250
),
dashboardSidebar(
sidebarMenu(
menuItem("Calculator ", tabName = "calculator", icon = icon("calculator"))
)
),
#
dashboardBody(
tabItems(
tabItem(tabName = "calculator",
h1("Calculator"),
fluidRow(
column(width = 1,
selectInput("device","Device:",
#choices= Results()$Devices,
multiple=TRUE, selectize=TRUE)
),
column(width = 1,
selectInput("product","Product:",
#"choices= Results()$Products",
multiple=TRUE, selectize=TRUE)
)
),
fluidRow(
column(width = 6,
box(title="Overall Conversion rate %",status="primary",solidHeader = TRUE,
htmlOutput("CRABCalcl"),width = "100%",height=275)
),
column(width = 6,
box(title="Overall AOV £",status="primary",solidHeader = TRUE,
htmlOutput("AOVABCalcl"),width = "100%",height=275)
)
),
fluidRow(
column(width = 6,
box(title="Ecommerce Conversion rate %",status="primary",solidHeader = TRUE,
htmlOutput("CRABCalclEHC"),width = "100%",height=275)
),
column(width = 6,
box(title="Ecoomerce AOV £",status="primary",solidHeader = TRUE,
htmlOutput("AOVABCalclEHC"),width = "100%",height=275)
)
)
)
)#End of tab Item
) #end of tabItems
)#End of Dashboard body
)#End of dashboardPage
server <- function(input, output,session) {
Results <- reactive({
myDataRAW<-data.frame(
c("mobile","mobile","desktop","desktop","tablet","tablet"),
c("Control","Challenger","Control","Challenger","Control","Challenger"),
c(34355,34917,28577,29534,15337,13854),
c(15011,15427,32190,32548,40299,40858),
c(14636,14990,19609,19702,7214,7785),
c(123273.70,20936.92,45179.05,46359.91,65765.27,92771.36),
c(10370,13403,19241,26965,4468,8796)
)
myDataRAWEHC<-data.frame(
c("desktop","desktop","mobile","mobile","tablet","tablet","desktop","desktop","mobile","mobile","desktop","desktop","mobile","mobile","tablet","tablet","tablet","tablet","desktop","desktop"),
c("Card","Card","Card","Card","Card","Card","Card","Card","Gift","Gift","Gift","Card","Card","Card","Card","Card","Card","Card","Flower","Flower"),
c("Standard","Standard","Standard","Standard","Standard","Standard","Large","Large","Large","Large","Square","Square","Square","Square","Large","Large","Square","Square","Flowers","Flowers"),
c("Control","Challenger","Control","Challenger","Control","Challenger","Control","Challenger","Control","Challenger","Control","Challenger","Control","Challenger","Control","Challenger","Control","Challenger","Control","Challenger"),
c(8767,18072,5729,13017,2908,7086,1655,2971,1008,2177,984,2369,599,1422,449,1052,402,1001,233,355),
c(9055,18624,5908,13302,3015,7288,1691,3000,1013,2192,1009,2455,623,1450,455,1068,413,1017,233,356),
c(21699.60,44480.95,14464.85,32590.30,7232.47,17483.35,8309.85,14684.68,5024.92,10844.67,2405.07,5826.83,1529.16,3556.38,2220.21,5192.92,992.14,2447.78,5196.08,8021.95)
)
names(myDataRAW)<-c("Device.Category","Segment","Users","Sessions","Transactions","Revenue","Quantity")
names(myDataRAWEHC)<-c("Device.Category","Product.Category..Enhanced.Ecommerce.","Product.Variant","Segment","Unique.Purchases","Quantity","Product.Revenue")
Devices<-myDataRAW$Device.Category
Products<-unique(myDataRAWEHC$Product.Category..Enhanced.Ecommerce.)
# DeviceFilter<-input$device
# ProductFilter<-input$product
#the below is replacing the above input to act as filters
DeviceFilter<-c("desktop","mobile")
ProductFilter<-c("Flower","Gift")
myData<-myDataRAW %>% filter(Device.Category %in% DeviceFilter)
myDataEHC<-myDataRAWEHC %>% filter(Device.Category %in% DeviceFilter) %>% filter(`Product.Category..Enhanced.Ecommerce.` %in% ProductFilter)
myData<-bind_rows(myData,myData %>% group_by(Device.Category="All",Segment) %>% summarise(Users=sum(Users),Sessions=sum(Sessions),Transactions=sum(Transactions),Revenue=sum(Revenue),Quantity=sum(Quantity)))
myDataEHC<-rbind(myDataEHC %>% group_by(Device.Category,Segment) %>% summarise(Transactions=sum(Unique.Purchases),Quantity=sum(Quantity),Revenue=sum(Product.Revenue)),
myDataEHC %>% group_by(Device.Category="All",Segment) %>% summarise(Transactions=sum(Unique.Purchases),Quantity=sum(Quantity),Revenue=sum(Product.Revenue)) )
myDataEHC<-left_join(myDataEHC,myData %>% select(Segment,Device.Category,Users,Sessions))
myData$Analysis<-"Overall"
myDataEHC$Analysis<-"Ecommerce"
myDataForAnalysis<-rbind(as.data.frame(myData),as.data.frame(myDataEHC))
myDataForAnalysis$CVR<-myDataForAnalysis$Transactions/myDataForAnalysis$Sessions
myDataForAnalysis$AOV<-myDataForAnalysis$Revenue/myDataForAnalysis$Transactions
DisplayResultsEHC<-myDataForAnalysis %>% filter(Analysis %in% "Ecommerce")
DisplayResults<-myDataForAnalysis %>% filter(Analysis %in% "Overall")
list(DisplayResultsEHC=DisplayResultsEHC,DisplayResults=DisplayResults,Devices=Devices,Products=Products)
})
output$CRABCalcl <- renderGvis({
DataABCalcl<-Results()$DisplayResults
F<-cast(DataABCalcl, Device.Category~Segment, value = 'CVR')
X<-gvisColumnChart(F,options=list(legend="{position:'top'}",width="100%"))
})
output$AOVABCalcl <- renderGvis({
DataABCalcl<-Results()$DisplayResults
F<-cast(DataABCalcl, Device.Category~Segment, value = 'AOV')
X<-gvisColumnChart(F,options=list(legend="{position:'top'}",width="100%"))
})
output$CRABCalclEHC <- renderGvis({
DataABCalcl<-Results()$DisplayResultsEHC
F<-cast(DataABCalcl, Device.Category~Segment, value = 'CVR')
X<-gvisColumnChart(F,options=list(legend="{position:'top'}",width="100%"))
})
output$AOVABCalclEHC <- renderGvis({
DataABCalcl<-Results()$DisplayResultsEHC
F<-cast(DataABCalcl, Device.Category~Segment, value = 'AOV')
X<-gvisColumnChart(F,options=list(legend="{position:'top'}",width="100%"))
})
}
shinyApp(ui, server)
From what I see a good start would be to create a global.R file containing (and remove from server.R):
global.R
myDataRAW<-data.frame(
c("mobile","mobile","desktop","desktop","tablet","tablet"),
c("Control","Challenger","Control","Challenger","Control","Challenger"),
c(34355,34917,28577,29534,15337,13854),
c(15011,15427,32190,32548,40299,40858),
c(14636,14990,19609,19702,7214,7785),
c(123273.70,20936.92,45179.05,46359.91,65765.27,92771.36),
c(10370,13403,19241,26965,4468,8796)
)
myDataRAWEHC<-data.frame(
c("desktop","desktop","mobile","mobile","tablet","tablet","desktop","desktop","mobile","mobile","desktop","desktop","mobile","mobile","tablet","tablet","tablet","tablet","desktop","desktop"),
c("Card","Card","Card","Card","Card","Card","Card","Card","Gift","Gift","Gift","Card","Card","Card","Card","Card","Card","Card","Flower","Flower"),
c("Standard","Standard","Standard","Standard","Standard","Standard","Large","Large","Large","Large","Square","Square","Square","Square","Large","Large","Square","Square","Flowers","Flowers"),
c("Control","Challenger","Control","Challenger","Control","Challenger","Control","Challenger","Control","Challenger","Control","Challenger","Control","Challenger","Control","Challenger","Control","Challenger","Control","Challenger"),
c(8767,18072,5729,13017,2908,7086,1655,2971,1008,2177,984,2369,599,1422,449,1052,402,1001,233,355),
c(9055,18624,5908,13302,3015,7288,1691,3000,1013,2192,1009,2455,623,1450,455,1068,413,1017,233,356),
c(21699.60,44480.95,14464.85,32590.30,7232.47,17483.35,8309.85,14684.68,5024.92,10844.67,2405.07,5826.83,1529.16,3556.38,2220.21,5192.92,992.14,2447.78,5196.08,8021.95)
)
names(myDataRAW)<-c("Device.Category","Segment","Users","Sessions","Transactions","Revenue","Quantity")
names(myDataRAWEHC)<-c("Device.Category","Product.Category..Enhanced.Ecommerce.","Product.Variant","Segment","Unique.Purchases","Quantity","Product.Revenue")
This allows you to access myDataRAW and myDataRAWEHC from ui.R. Modify the ui.R accordingly:
fluidRow(
column(width = 3,
selectInput("device","Device:",
choices= levels(myDataRAW$Device.Category),
multiple=TRUE, selectize=TRUE)
),
column(width = 3,
selectInput("product","Product:",
choices= unique(levels(myDataRAWEHC$Product.Category..Enhanced.Ecommerce.)),
multiple=TRUE, selectize=TRUE)
)
),
after that you still have some work left to rearrange the server.R part.
I'm building a new Shiny app and I although it works, the code is too extensive and it is not as reactive as I wanted. Right now I have at server.R
dayData <- reactive({...})
pday <- function(data){...}
output$distPlotday <- renderPlot(function() {print(pday(dayData)) })
and at ui.R
plotOutput("distPlotday")
for each variable in
checkboxGroupInput("checkGroup", "Dataset Features:",
choices = c("day","hour","source","service","relevancy","tollfree","distance","similarity"))
But I wish I could do something more fancy like this:
shinyServer(function(input, output, session) {
...
output$sliders <- renderUI({
lapply(input$checkGroup, function(i) {
fluidRow(
column(4,
selectInput(paste0('trans',i), i,
choices = c('linear','quadratic','sine')) ,
conditionalPanel(
condition = "input[[paste0('trans',i)]]== 'sine'",
withMathJax(),
h5("Put in your initial kicks for: $$a*\\sin(b*x+c)+d$$"),
textInput3(paste0('trans',i,'a'), h5('A:'),
value = 10),
textInput3(paste0('trans',i,'b'), h5('C:'),
value = 1),
textInput3(paste0('trans',i,'c'), h5('D:'),
value = 0.1),
helpText("Note: B has already been picked up")
),
plotOutput(paste0('distPlot',i))
))
})
})
...
}))
.
shinyUI(navbarPage("",
tabPanel("Data",
sidebarLayout(
sidebarPanel(
checkboxGroupInput("checkGroup", label = h5("Dataset Features:"),
choices = c("day","hour","source","service","relevancy","tollfree","distance","similarity"), inline = F,
selected = c("day","hour","source","service","relevancy","tollfree","distance","similarity"))
),
mainPanel(
numericInput("obs", label = h5("Number of observations to view"), 15, min = 10, max = 20, step = 1),
tableOutput("view")
)
)
),
tabPanel("Variable transformation", uiOutput(outputId = "sliders"))
))
Using lapply and renderUI. But
plotOutput(paste0('distPlot',i))
is not ploting anything, and the
conditionalPanel(condition = "input[[paste0('trans',i)]]== 'sine'",...)
don't show up conditionally, instead it's always there.
Any suggestions? Thanks for the help!
I wasn't sure what you wanted to do with the plotOutput call, since as far as I can tell there wasn't any example code included that linked to it. However, I managed to put together a working example for dynamically showing/hiding the selection boxes and text fields for the sine parameters.
I found it easier to implement by moving the ui generation from the server into the ui. This gets around the problem of conditions being evaluated for input that doesn't exist yet, since on the ui side the functions are just writing html.
An additional benefit is that this way the input fields don't get re-rendered every time the checkbox input changes - this means that their values persist through toggling them on and off, and that enabling or disabling a single variable won't cause the others' values to reset.
The code:
library(shiny)
vars <- c("day","hour","source","service","relevancy",
"tollfree","distance","similarity")
ui <- shinyUI(navbarPage("",
tabPanel("Data",
sidebarLayout(
sidebarPanel(
checkboxGroupInput("checkGroup", label = h5("Dataset Features:"),
choices = c("day","hour","source","service","relevancy",
"tollfree","distance","similarity"), inline = F,
selected = c("day", "hour","source","service","relevancy",
"tollfree","distance","similarity")
)
),
mainPanel(
numericInput("obs", label = h5("Number of observations to view"),
value = 15, min = 10, max = 20, step = 1),
tableOutput("view")
)
)
),
tabPanel("Variable transformation",
fluidRow(
column(4,
lapply(vars, function(i) {
div(
conditionalPanel(
condition =
# javascript expression to check that the box for
# variable i is checked in the input
paste0("input['checkGroup'].indexOf('", i,"') != -1"),
selectInput(paste0('trans',i), i,
choices = c('linear','quadratic','sine'))
),
conditionalPanel(
condition =
paste0("input['trans", i, "'] == 'sine' ",
" && input['checkGroup'].indexOf('", i,"') != -1"),
withMathJax(),
h5("Put in your initial kicks for: $$a*\\sin(b*x+c)+d$$"),
textInput(paste0('trans',i,'a'), h5('A:'), value = 10),
textInput(paste0('trans',i,'b'), h5('C:'), value = 1),
textInput(paste0('trans',i,'c'), h5('D:'), value = 0.1),
helpText("Note: B has already been picked up")
)
)
})
)
)
)
))
server <- shinyServer(function(input, output, session) {})
shinyApp(ui, server)
PS. For dynamically showing/hiding or enabling/disabling objects, the package shinyjs by Dean Attali (link) has some nice tools that allow you to call basic javascript by using only R syntax.