How to organize large Shiny apps?
After addition of modules to R shiny. Managing of complex structures in shiny applications has become a lot easier.
Detailed description of shiny modules:Here
Advantages of using modules:
- Once created, they are easily reused
- ID collisions is easier to avoid
- Code organization based on inputs and output of modules
In tab based shiny app, one tab can be considered as one module which has inputs and outputs. Outputs of tabs can be then passed to other tabs as inputs.
Single-file app for tab-based structure which exploits modular thinking. App can be tested by using cars dataset. Parts of the code where copied from the Joe Cheng(first link). All comments are welcome.
# Tab module
# This module creates new tab which renders dataTable
dataTabUI <- function(id, input, output) {
# Create a namespace function using the provided id
ns <- NS(id)
tagList(sidebarLayout(sidebarPanel(input),
mainPanel(dataTableOutput(output))))
}
# Tab module
# This module creates new tab which renders plot
plotTabUI <- function(id, input, output) {
# Create a namespace function using the provided id
ns <- NS(id)
tagList(sidebarLayout(sidebarPanel(input),
mainPanel(plotOutput(output))))
}
dataTab <- function(input, output, session) {
# do nothing...
# Should there be some logic?
}
# File input module
# This module takes as input csv file and outputs dataframe
# Module UI function
csvFileInput <- function(id, label = "CSV file") {
# Create a namespace function using the provided id
ns <- NS(id)
tagList(
fileInput(ns("file"), label),
checkboxInput(ns("heading"), "Has heading"),
selectInput(
ns("quote"),
"Quote",
c(
"None" = "",
"Double quote" = "\"",
"Single quote" = "'"
)
)
)
}
# Module server function
csvFile <- function(input, output, session, stringsAsFactors) {
# The selected file, if any
userFile <- reactive({
# If no file is selected, don't do anything
validate(need(input$file, message = FALSE))
input$file
})
# The user's data, parsed into a data frame
dataframe <- reactive({
read.csv(
userFile()$datapath,
header = input$heading,
quote = input$quote,
stringsAsFactors = stringsAsFactors
)
})
# We can run observers in here if we want to
observe({
msg <- sprintf("File %s was uploaded", userFile()$name)
cat(msg, "\n")
})
# Return the reactive that yields the data frame
return(dataframe)
}
basicPlotUI <- function(id) {
ns <- NS(id)
uiOutput(ns("controls"))
}
# Functionality for dataselection for plot
# SelectInput is rendered dynamically based on data
basicPlot <- function(input, output, session, data) {
output$controls <- renderUI({
ns <- session$ns
selectInput(ns("col"), "Columns", names(data), multiple = TRUE)
})
return(reactive({
validate(need(input$col, FALSE))
data[, input$col]
}))
}
##################################################################################
# Here starts main program. Lines above can be sourced: source("path-to-module.R")
##################################################################################
library(shiny)
ui <- shinyUI(navbarPage(
"My Application",
tabPanel("File upload", dataTabUI(
"tab1",
csvFileInput("datafile", "User data (.csv format)"),
"table"
)),
tabPanel("Plot", plotTabUI(
"tab2", basicPlotUI("plot1"), "plotOutput"
))
))
server <- function(input, output, session) {
datafile <- callModule(csvFile, "datafile",
stringsAsFactors = FALSE)
output$table <- renderDataTable({
datafile()
})
plotData <- callModule(basicPlot, "plot1", datafile())
output$plotOutput <- renderPlot({
plot(plotData())
})
}
shinyApp(ui, server)
R [Shiny]: How to make reactive shiny apps which display dynamic systems models?
Needed some minor adjustments. Try this
library(shiny)
library(deSolve)
library(ggplot2)
library(gridExtra)
ui <- fluidPage(
sliderInput("iDesired.Growth", "Desired.Growth", min = 0, max = 0.15, step = 0.01, value = 0.07),
sliderInput("iDepreciation", "Depreciation", min = 0, max = 0.15, step = 0.01, value = 0.07),
plotOutput(outputId = "arrange")
)
server <- function(input, output, session) {
growth <- reactiveVal(1)
dep <- reactiveVal(1)
START <-0; FINISH<-200; STEP<-0.25
simtime <- seq(START, FINISH, by = STEP)
stocks <- c(sCapital=5, sResource=1000)
x.Resource <- seq(0,1000, by=100)
y.Efficiency<- c(0,0.25,0.45,0.63,0.75,0.86,0.92, 0.96,0.98, 0.99,1.0)
func.Efficiency <- approxfun(x=x.Resource,
y=y.Efficiency,
method = "linear",
yleft = 0, yright = 1.0)
observe({
model <- function(time,stocks,auxs){
with(as.list(c(stocks,auxs)),{
aExtr.Efficiency <- func.Efficiency(sResource)
fExtraction <- aExtr.Efficiency*sCapital
aTotal.Revenue <- aRevenue.Per.Unit * fExtraction
aCapital.Costs <- sCapital *0.1
aProfit <- aTotal.Revenue - aCapital.Costs
aCapital.Funds <- aFraction.Reinvested * aProfit
aMaximum.Investment <- aCapital.Funds/aCost.Per.Investment
aDesired.Investment <- sCapital * aDesired.Growth
fInvestment <- min(aMaximum.Investment,
aDesired.Investment)
fDepreciation <- sCapital * aDepreciation
dS_dt <- fInvestment -fDepreciation
dR_dt <- -fExtraction
return(list(c(dS_dt, dR_dt),
DesiredInvestment=aDesired.Investment,
MaximumInvestment=aMaximum.Investment,
Investment=fInvestment,
Depreciation=fDepreciation,
Extraction=fExtraction))
})
}
growth(input$iDesired.Growth)
dep(input$iDepreciation)
auxs <- list(aDesired.Growth = growth(),
aDepreciation = dep(),
aCost.Per.Investment = 2,
aFraction.Reinvested =0.12,
aRevenue.Per.Unit =3.00)
o <- data.frame(ode(y=stocks, times=simtime, func = model,
parms = auxs, method = "euler"))
flow_plot <- ggplot(data = o, mapping = aes(time, Investment)) + theme_classic() +
geom_line(data = o, mapping = aes(time, Investment), size = 1, color = "blue", linetype =2)+
geom_line(data = o, mapping = aes(time, Depreciation), size = 1, color = "red",linetype =2)+
geom_line(data = o, mapping = aes(time, Investment-Depreciation), size = 1, color = "black")
f <- renderPlot({
flow_plot <- ggplot(data = o, mapping = aes(time, Investment)) + theme_classic() +
geom_line(data = o, mapping = aes(time, Investment), size = 1, color = "blue", linetype =2)+
geom_line(data = o, mapping = aes(time, Depreciation), size = 1, color = "red",linetype =2)+
geom_line(data = o, mapping = aes(time, Investment-Depreciation), size = 1, color = "black")
})
capital_plot <- ggplot(data = o, mapping = aes(time, sCapital)) + theme_classic() +
geom_line(data = o, mapping = aes(time, sCapital), size = 1, color = "blue", linetype =2)+
geom_line(data = o, mapping = aes(time, Extraction), size = 1, color = "black")
ressource_plot <- ggplot(data = o, mapping = aes(time, sCapital)) + theme_classic() +
geom_line(data = o, mapping = aes(time, sResource), size = 1, color = "black", linetype =1)
output$arrange <- renderPlot({
grid.arrange(flow_plot,capital_plot,ressource_plot, nrow = 3)
})
})
}
shinyApp(ui, server)
How are you handling theme updates across multiple Shiny apps?
For those who might be interested, I built the theme into a package I was developing. Within my package's root dir, I added an inst
folder and, inside that, a www
dir. I added the organisation logo and some extra CSS that couldn't be easily configured with {fresh}
.
To set the theme, I created a function like this:
set_theme<-function(){
theme<-fresh::create_theme(
fresh::adminlte_color(
light_blue = "#383F48",
aqua = "#094357",
green = "#094357",
blue = "#383F48"
),
fresh::adminlte_global(
box_bg = "#FFFFFF",
info_box_bg = "#D1E0E5"
),
fresh::adminlte_vars(
"sidebar-width" = "275px",
"sidebar-dark-bg" = "#3A3F46",
"sidebar-dark-hover-color" = "#FFB151",
"btn-border-radius" = "1px"
)
)
shiny::addResourcePath('www', system.file("www", package = "myPackage"))
return(theme)
}
I used shiny::addResourcePath
to create a resource path to link the package's inst/www
folder to the project within which myPackage::set_theme()
is called. Then, within my Shiny dashboards:
library(myPackage)
theme <- myPackage::set_theme()
ui<-(
fresh::use_theme(theme)
...
tags$head(tags$link(rel = "stylesheet", type = "text/css", href =
"www/style.css"))
...
)
Make note of the www/
in the href argument. If you were to keep these files 'locally' within your Shiny app, you'd create a www
dir in root and forego the www/
prefix. When calling files from your package, simply include the www/
How to convert a piece of R code into Shiny apps?
My first suggestion is just to look at tutorials on shiny, they give a great overview on how to start a project: https://shiny.rstudio.com/tutorial/
I didn't know a thing about programming a few years back, so I understand it can be hard figuring out where to start, so I wanted to give you an idea of how to implement a function, and use shiny inputs to make the resulting table/plot be dynamic.
I switched up your code to be easier to reproduce for myself. I hope this gives you the starting point you need:
library(tidyverse)
library(ggplot2)
library(shiny)
WR_sim_OC <- function(MPG, CYL, DISP){
results <- mtcars%>% #Function to make a table
filter(cyl > CYL,
mpg > MPG,
disp > DISP)
out1 <- ggplot(data = results, aes(x = mpg, y = disp, group = cyl)) +
geom_line(aes(color = hp), size = 1) #Function to make a plot
list(results, out1) #List to create table and function
}
ui <- fluidPage(
numericInput("MilesPerGallon", "mpg", value = 15),
numericInput("Cylinders", "cyl", value = 4),
numericInput("Displacement", "disp", value = 200),
tableOutput("TABLE"),
plotOutput("PLOT")
)
server <- function(input, output, session) {
output$TABLE<-renderTable({
req(input$MilesPerGallon, input$Cylinders, input$Displacement) #Requires all three inputs before it makes the table
WR_sim_OC(input$MilesPerGallon, input$Cylinders, input$Displacement)[1] #Only pulling the table from the function
})
output$PLOT<-renderPlot({
req(input$MilesPerGallon, input$Cylinders, input$Displacement) #Requires all three inputs before it makes the plot
WR_sim_OC(input$MilesPerGallon, input$Cylinders, input$Displacement)[2] #Only pulling the plot from the function
})
}
shinyApp(ui, server)
Essentially on the server side where you render the plot or table, you use those inputs from the ui as the dynamic points in your function. I used req() for both of the renderTable and renderPlot to make sure the inputs are filled out before it makes the table plot. Best of luck!
R Shiny: Using a Large SpatialPolygonsDataFrame in my Shiny App
When the Shiny app is launched, it looks only for server.R
and ui.R
(see ?shiny::runApp
). load_data.R
is thus not sourced.
Try adding source("load_data.R")
to server.R
.
Music files do not play in shiny
In order to use music from the new directory, you will also need to change addResourcePath
to the relevant path. In this case, if you use addResourcePath("Music", "Music")
this should be enough for it to work.
Related Topics
Lookup Values Corresponding to the Closest Date
What Are Some Good Books, Web Resources, and Projects for Learning R
How to Save a Data Frame as CSV to a User Selected Location Using Tcltk
How to Ignore Case When Using Str_Detect
How to Syntax Highlight Inline R Code in R Markdown
Creating Tree Diagram for Showing Case Count Using R
Overlaying Two Graphs Using Ggplot2 in R
How to Install Dependencies When Using "R Cmd Install" to Install R Packages
Convert String Back into Object in R
Ggplot2 Legend to Bottom and Horizontal
Controlling the 'Alpha' Level in a Ggplot2 Legend
How to Get Currency Exchange Rates in R
Boxplot Schmoxplot: How to Plot Means and Standard Errors Conditioned by a Factor in R