I am building a web app using shiny, and I'm unsure how to best structure the app since inputs depend on data and the outputs (charts) depend on aggregated data based on inputs.
I tried to come up with a simple application to reproduce the problem. My setup is more advanced and unrelated to the example. Suppose you have a product line and want to analyse sales. Suppose that a data set is created for each day (I'm not saying the data structure is optimal, but it is useful for illustrating my question). Now in the application, one selects a date from a list of available dates, and then one selects a product. The dates are restricted to the period for which data is available, and the product list is restricted to products that were actually sold on the selected day. Then, we wish to plot the total sales value for each hour during the day.
I will list some code for such an example below, where some sample data is also created. Sorry for the "long" code. It is sort of working, but I have some concerns.
My questions are:
1) I am wondering in which order things are executed, in particular when the app is first loaded, and then every time an input changes. Again, data depends on first input, the second input depends on the data. Third, a chart-friendly data set is computed which is used for the graph. You may notice that errors are printed to the console (and flashes briefly in the browser) but as the values are available, updates are made and the plot shows. It seems suboptimal.
2) What is the current best practice when the inputs depend on data/server.R? I saw this https://groups.google.com/forum/?fromgroups=#!topic/shiny-discuss/JGJx5A3Ge-A but it seems like this is not implemented, even thought the post is rather old.
Here is the code for the two files:
# ui.R
######
library(shiny)
shinyUI(pageWithSidebar(
headerPanel("New Application"),
sidebarPanel(
htmlOutput("dateInput"),
htmlOutput("prodInput")
),
mainPanel(
plotOutput("salesplot")
)
))
And:
#server.R
#########
library(shiny)
library(filehash)
set.seed(1)
dates <- format(seq(Sys.Date() - 10, Sys.Date(), "days"), "%Y-%m-%d")
products <- LETTERS
prices <- sample(10:100, size = length(products), replace = TRUE)
names(prices) <- LETTERS
if (file.exists("exampledb")) {
db <- dbInit("exampledb")
} else {
dbCreate("exampledb")
db <- dbInit("exampledb")
for (d in dates) {
no.sales <- sample(50:100, size = 1)
x <- data.frame(
product = sample(products, size = no.sales, replace = TRUE)
,hour = sample(8:20, size = no.sales, replace = TRUE)
,order.size = sample(1:10, size = no.sales, replace = TRUE)
)
x$price <- prices[x$product]
dbInsert(db, paste0("sales", gsub("-", "", d)), x)
}
}
current <- reactiveValues()
shinyServer(function(input, output) {
inputDates <- reactive({
sort(strptime(unique(substr(names(db), 6, 13)), "%Y%m%d"))
})
output$dateInput <- renderUI({ dateInput(
inputId = "date",
label = "Choose hour",
min = min(inputDates()),
max = max(inputDates()),
format = "yyyy-mm-dd",
startview = "month",
weekstart = 0,
language = "en")
})
inputProducts <- reactive({
current$data <<- dbFetch(db, paste0("sales", format(input$date, "%Y%m%d")))
sort(unique(current$data$product))
})
output$prodInput <- renderUI({ selectInput(
inputId = "product",
label = "Choose Product",
choices = inputProducts(),
selected = 1)
})
output$salesplot <- renderPlot({
pdata <- aggregate(I(order.size*price) ~ hour,
data = subset(current$data, product == input$product),
FUN = sum)
colnames(pdata)[2] <- "value"
plot(value ~ hour, data = pdata, xlim = c(8, 20))
})
})
It seems like this would a good place to use global.R. The global.R file gets read before ui.R and server.R, so you could have data pulls from global accessible to both the ui and server.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With