Hello,
I am hoping you can help with this question.
Background/Use Case: I am trying develop a shiny app so that management can quickly visualize forecast vs actual sales of various products within a company. The company has multiple products. Here is the slightly tricky part: although each product is attached to a single column of data for actual sales; the data for forecast sales may be a single column or multiple columns.
Key Question: Based on a single selection from selectInput
in ui
; I want render either 1 or 2 plots based on type of data that is associated with that choice.
Here is simple example, hopefully this serves the purpose of a reprex.
# raw data
df <- tibble(
date = as.Date(c("2018-03-01", "2018-06-01", "2018-09-01", "2018-12-01")),
ProductA_Actual = c(10, 12, 9, 14),
ProductA_Forecast1 = c(9, 11, 10, 15),
ProductB_Actual = c(20, 22, 27, 24),
ProductB_Forecast1 = c(19, 21, 28, 23),
ProductB_Forecast2 = c(22, 18, 28, 30)
)
# Converted to 'long' format
df_long <- df %>%
gather(Product_Type, Sales, -date) %>%
separate(Product_Type, c("Product", "Data_Type"), "_")
df_long
Shiny: Here is what I have written so far.
library(tidyverse)
library(shiny)
# UI
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("Product", "Select Product", choices=as.character(unique(df_long$Product)), selected="ProductA")
),
mainPanel(
h3("Sales: Forecasts vs Actual"),
plotOutput("plot_1"),
plotOutput("plot_2")
)
)
)
## server
server <- function(input, output) {
output$plot_1 <- renderPlot({
df_long %>%
filter(Product == input$Product) %>%
filter(Data_Type %in% c("Actual" , "Forecast1")) %>%
ggplot(aes(x=date)) +
geom_line(aes(y=Sales, color=Data_Type),size=1) +
geom_hline(yintercept = 0) +
scale_x_date(breaks=unique(df_long$date), date_labels = "%b-%y") +
theme_bw() +
theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position="right", legend.title = element_blank()) +
labs(y="Sales Percentage Change Y/Y", x="Quarter") +
ggtitle(label="Chart 1: Actual vs Forecast 1")
})
output$plot_2 <- renderPlot({
df_long %>%
filter(Product == input$Product) %>%
filter(Data_Type %in% c("Actual" , "Forecast2")) %>%
ggplot(aes(x=date)) +
geom_line(aes(y=Sales, color=Data_Type),size=1) +
geom_hline(yintercept = 0) +
scale_x_date(breaks=unique(df_long$date), date_labels = "%b-%y") +
theme_bw() +
theme(axis.text.x = element_text(angle = 45, hjust = 1), legend.position="right", legend.title = element_blank()) +
labs(y="Sales Percentage Change Y/Y", x="Quarter") +
ggtitle(label="Chart 2: Actual vs Forecast 2")
})
}
shinyApp(ui=ui, server=server)
When ProductB is selected, the charts are displayed in the way that I want:
However, when ProductA is selected, I end up with an incomplete chart at the bottom. (ProductA only has one forecast, ProductB has two forecasts)
How can I re-write this so that a single selection of product will lead to either 1 or 2 (or more) plots based on the number of forecasts associated with that product? I am assuming that some combination of data wrangling/reshaping and some of the more advanced features in shiny (conditional panel? reactive?) will get me there, but I just cant seem to figure this out.
Thanks in advance!!