class: center, middle, inverse, title-slide .title[ # Building Shiny Apps ] .author[ ### MACS 20400
University of Chicago ] --- # What is Shiny? * R package from RStudio * Web application framework for R * R code `\(\rightarrow\)` interactive web page * No HTML/CSS/Javascript knowledge required * Great for sharing R analysis with someone scared of R --- # Dating age rule * [Half your age plus seven](https://www.netflix.com/watch/70152033?t=420) * [My Shiny app](https://bensoltoff.shinyapps.io/cfss-age-rule/) --- # What is a Shiny app? * Computer running R * Web page * Computer performs calculations, sends contents to web page * User interacts with web page, sends updates back to computer * Rinse and repeat --- # City of Chicago wage employees * [Dataset](https://data.cityofchicago.org/Administration-Finance/Current-Employee-Names-Salaries-and-Position-Title/xzkq-xp2w) * [Final Shiny app](https://bensoltoff.shinyapps.io/chicago-employees/) --- # Shiny app template ```r library(shiny) ui <- fluidPage() server <- function(input, output) {} shinyApp(ui = ui, server = server) ``` **Important**: Do not place any code after `shinyApp()` --- # Run Shiny app in RStudio, method 1 Save file as **app.R** `\(\rightarrow\)` "Run" button turns to "Run App" ![](/img/shiny-runapp.png) Good for creating Shiny apps quickly, all code in one file --- # Run Shiny app in RStudio, method 2 Save UI as **ui.R** and server as **server.R** in **same directory** ![](/img/Shiny-run-app-screenshot.png) Good for complex Shiny apps, separates view vs logic If using this method, **do not** include a call to `shinyApp(...)` --- # Run Shiny app in RStudio, method 3 *File > New File > Shiny Web App...* ![](/img/shiny-add-template.png) Generates the template for you --- # Stop Shiny app in RStudio Press "Esc" or click the *Stop* icon ![](/img/shiny-stopapp.png) --- # Exercise: create the basic app * `usethis::use_course("uc-cfss/shiny-demo")` * Create a basic Shiny app * Import the City of Chicago wage employee data file --- # Add elements to app inside `fluidPage()` ```r library(shiny) ui <- fluidPage("Hello CFSS") server <- function(input, output) {} shinyApp(ui = ui, server = server) ``` ![](/img/shiny-basic.png) --- # Add elements to app inside `fluidPage()` ```r fluidPage( h1("My Shiny app"), "Hello CFSS" ) ``` ![](/img/shiny-add-h1.png) --- # Add HTML to `fluidPage()` * Remember the UI simply creates HTML * [Can use any HTML tags](http://shiny.rstudio.com/articles/tag-glossary.html) * `h1()` = header1 * `br()` = line break * `strong()` = bold text * Any HTML tag can be accessed using `tags` object * `h1` = `tags$h1()`, `br` = `tags$br()` * Common tags can be accesed without `tags` --- # Add HTML to `fluidPage()` ```r fluidPage( h1("My Shiny app"), h3("Subtitle"), "Hello", "CFSS", br(), strong("bold text") ) ``` ![](/img/shiny-tags.png) --- # Use a layout * By default, all elements stack up one after the other * [Can use different layouts](http://shiny.rstudio.com/articles/layout-guide.html) * We’ll use `sidebarLayout()` --- # `sidebarLayout()` ```r fluidPage( titlePanel("My Shiny app"), sidebarLayout( sidebarPanel( "This is a side panel" ), mainPanel( "And this is the main stuff" ) ) ) ``` --- # `sidebarLayout()` ![](/img/shiny-sidebarlayout.png) --- # Exercise: add a layout * Define the layout for the application * Add an app title * Identify where inputs and outputs will go --- # Inputs and outputs * For interactivity, app needs inputs and outputs * **Inputs** - things user can toggle/adjust * **Output** - R objects user can see, often depend on inputs --- # Inputs ```r library(shiny) ui <- fluidPage( sliderInput( "num", "Choose a number", min = 0, max = 100, value = 20) ) server <- function(input, output) {} shinyApp(ui = ui, server = server) ``` ![](/img/shiny-input.png) --- # Inputs ```r sliderInput("num", "Choose a number", min = 0, max = 100, value = 20) ``` ``` ## <div class="form-group shiny-input-container"> ## <label class="control-label" id="num-label" for="num">Choose a number</label> ## <input class="js-range-slider" id="num" data-skin="shiny" data-min="0" data-max="100" data-from="20" data-step="1" data-grid="true" data-grid-num="10" data-grid-snap="false" data-prettify-separator="," data-prettify-enabled="true" data-keyboard="true" data-data-type="number"/> ## </div> ``` --- # Inputs ![](/img/shiny-inputs.png) --- # Inputs .pull-left[ ```r sliderInput("num", "Choose a number", min = 0, max = 100, value = 20) ``` ] .pull-right[ * Input name * Label to display * Input-specific arguments ] --- # Exercise: add inputs * Add inputs for * Wage * Full/part-time * Department --- # Outputs Function | Outputs ---------|--------- `plotOutput()` | plot `tableOutput()` | table `uiOutput()` | Shiny UI element `textOutput()` | text * Plots, tables, text - anything that R creates and users see * Initialize as empty placeholder space until object is created --- # Outputs ```r library(shiny) ui <- fluidPage( sliderInput("num", "Choose a number", 0, 100, 20), plotOutput("myplot") ) server <- function(input, output) {} shinyApp(ui = ui, server = server) ``` ![](/img/shiny-addplot-placeholder.png) --- # Exercise: add placeholders for output * Add placeholders for a * Histogram * Table --- # Server: assemble input into outputs ```r server <- function(input, output) { output$myplot <- renderPlot({ plot(rnorm(input$num)) }) } ``` 1. Save objects into `output$` 1. Build objects with `render*()` --- # `Output()` `\(\rightarrow\)` `render*()` Output function | Render function -------|---------- `plotOutput()` | `renderPlot({})` `tableOutput()` | `renderTable({})` `uiOutput()` | `renderUI({})` `textOutput()` | `renderText({})` --- # `render*()` functions ```r renderPlot({ plot(rnorm(100)) }) ``` --- # Server: assemble input into outputs ```r server <- function(input, output) { output$myplot <- renderPlot({ plot(rnorm(input$num)) # in UI:sliderInput("num", ...) }) } ``` 1. Save objects into `output$` 1. Build objects with `render*()` 1. Access input values with `input$` --- # Exercise: add output * Create a histogram of hourly wages for selected employees * Add a table showing the count of selected employees per department --- # Reactivity * Shiny uses **reactive programming** * Reactive variables * When value of variable `x` changes, anything that relies on `x` is re-evaluated * Contrast with regular R: ```r x <- 5 y <- x + 1 x <- 10 ``` --- # Reactivity * `input$num` is a **reactive** value ```r output$myplot <- renderPlot({ plot(rnorm(input$num)) }) ``` * `output$myplot` depends on `input$num` * `input$num` changes `\(\rightarrow\)` `output$myplot` **reacts** * All inputs are automatically reactive, so if you use any input inside a `render*` function, the output will re-render any time input changes --- # Reactive contexts * You can define your own reactive variables * Reactive values can only be used inside **reactive contexts** * Any `render*` function is a reactive context * Use `reactive({...})` to assign a reactive variable * Use `observe({...})` to access a reactive variable * Remember: reactive variable means anything that depends on it gets re-executed automatically --- # Reactive contexts .pull-left[ ## Assign variable ```r server <- function(input, output) { x <- input$num + 1 } # error ``` ```r server <- function(input, output) { x <- reactive({ input$num + 1 }) } # OK ``` ] .pull-right[ ## Access variable ```r server <- function(input, output) { print(input$num) } # error ``` ```r server <- function(input, output) { observe({ print(input$num) }) } # OK ``` ] --- # Simple Shiny app using basic reactivity ```r library(shiny) ui <- fluidPage( sliderInput("num", "Choose a number", 0, 100, 20), plotOutput("myplot") ) server <- function(input, output) { output$myplot <- renderPlot({ plot(seq(input$num)) }) x <- reactive({ input$num + 1 }) observe({ print(x()) }) } shinyApp(ui = ui, server = server) ``` --- # Exercise: make your code more efficient * Create a reactive data frame `employ_filter` that creates the filtered data frame * Use `employ_filter` to create the histogram and table --- # Create UI elements dynamically * `uiOutput()` * Changing input values based on other inputs --- # Basic example of uiOutput() ```r library(shiny) ui <- fluidPage( numericInput("num", "Maximum slider value", 5), uiOutput("slider") ) server <- function(input, output) { output$slider <- renderUI({ sliderInput("slider", "Slider", min = 0, max = input$num, value = 0) }) } shinyApp(ui = ui, server = server) ``` --- # Exercise: Populate the job titles * Use `uiOutput()` in our app to populate the job titles input --- # Share your app: shinyapps.io * Go to http://www.shinyapps.io/ and make an account * Make sure all your app files are in an isolated folder * Click "Publish Application" in RStudio ![](/img/shiny-publish.png) * You might be asked to install a couple packages * Follow instructions from RStudio