class: center, middle ### Interactivity with `shiny` <img src="img/hero_wall_pink.png" width="800px"/> ### Kelly McConville .large[Math 241 | Week 11 | Spring 2021] --- ## Announcements/Reminders + You are going to do some live coding with me. + If you miss a step, get stuck, or just need some help, type your question into #in-class. * If you do miss anything: * I will put all the apps we create today in the shared folder. * Remember that I record all of our sessions. --- ## Interactivity with `shiny` `shiny`: Framework for creating web applications * We will go from R code in a script file to an interactive web page. * Let's look at some examples! + [Colleges](https://shiny.reed.edu/s/users/couchs/colleges/) + [Interactive Word Cloud](https://shiny.rstudio.com/gallery/word-cloud.html) + [Movie Explorer](https://shiny.rstudio.com/gallery/movie-explorer.html) + [The app we are going to create today](https://shiny.reed.edu/s/users/mcconville/app_names/) --- ## Main Components For the examples, what features of the apps did we interact with? -- For the examples, what changed based on our selections? -- * **Inputs**: What user manipulates + Text + Sliders + Dropdown menus + Action buttons * **Output**: What changes based on user's selections + Graphs + Maps + Tables + Text * Use **reactive programming** + Update outputs when inputs change --- ## Main Components * **UI**: User interface + Defines how your app **looks** -- * **Server function** + Defines how your app **works** -- ```r library(shiny) # User interface ui <- fluidPage() # Server function server <- function(input, output){} # Creates app shinyApp(ui = ui, server = server) ``` --- ## App Building Workflow Starting a New App: * Create a folder called "ShinyApps". * Within "ShinyApps", create a folder called "insert useful name". * Open a script file (not an Rmd) and save it as "app.r" within the "insert useful name" folder. -- Revising an App: * Write some code in "app.r". * Click "Run App". + Notice what happens in the console. * Experiment with the app. * Stop the app. * Repeat. -- Good habit early on: * Write static code somewhere (in a .r or .Rmd) before you start the app. + Modify to be reactive when put in "app.r". **Let's practice together!** --- ```r library(babynames) library(tidyverse) names <- c("Kelly", "Lucas", "Robin", "Sophia") dat_names <- babynames %>% group_by(year, name) %>% summarize(n = sum(n)) %>% group_by(year) %>% mutate(prop = n/sum(n)) %>% filter(name %in% names, year >= 1980) dat_names ``` ``` ## # A tibble: 152 x 4 ## # Groups: year [38] ## year name n prop ## <dbl> <chr> <int> <dbl> ## 1 1980 Kelly 14901 0.00433 ## 2 1980 Lucas 3097 0.000899 ## 3 1980 Robin 3250 0.000944 ## 4 1980 Sophia 641 0.000186 ## 5 1981 Kelly 13058 0.00377 ## 6 1981 Lucas 3394 0.000981 ## 7 1981 Robin 3323 0.000961 ## 8 1981 Sophia 1238 0.000358 ## 9 1982 Kelly 13440 0.00383 ## 10 1982 Lucas 2757 0.000786 ## # … with 142 more rows ``` --- We want to create an app where someone can type in names from Math 241 (or any names) and see how their popularities compare. * Input: <!-- Text --> * Output: <!-- Graph --> ```r ggplot(data = dat_names, mapping = aes(x = year, y = prop, color = name)) + geom_line(size = 2) ``` <img src="slidesWk11Tu_files/figure-html/unnamed-chunk-3-1.png" width="432" /> --- ```r library(shiny) # User interface ui <- fluidPage() # Server function server <- function(input, output){} # Creates app shinyApp(ui = ui, server = server) ``` --- ## Libraries * Make sure to put all the necessary libraries at the top of your app.R file. + Like Rmds, our apps must be self-contained. ```r # Libraries library(shiny) library(tidyverse) library(babynames) ``` --- ## Setting up the Room The `ui` functions generate HTML code for the web page. * `fluidPage()`: Layout function * `titlePanel()`: Adds title * `sidebarLayout()`: Splits app into side panel and main panel. <div class="figure"> <img src="img/sidebarLayout.png" alt="Source: Dean Attali" width="90%" /> <p class="caption">Source: Dean Attali</p> </div> --- ## Setting up the Room The `ui` functions generate HTML code for the web page. * `fluidPage()`: Layout function * `titlePanel()`: Adds title * `sidebarLayout()`: Splits app into side panel and main panel. ```r # User interface ui <- fluidPage( titlePanel(title = "Insert title"), sidebarLayout( sidebarPanel( ), mainPanel( ) ) ) ``` --- ## Input: Text * `textInput()`: Allow user to interact with the app by providing text ```r # User interface ui <- fluidPage( titlePanel("Which Math 241 name is most popular?"), sidebarLayout( sidebarPanel( # Create a text input widget textInput(inputId = "names", label = "Enter Math 241 names here", value = "Kelly"), p("Put single space between the names.") ), mainPanel( ) ) ) ``` --- ## Output: Graph * `plotOutput()`: Tells Shiny where to render the graph. ```r # User interface ui <- fluidPage( titlePanel("Which Math 241 name is most popular?"), sidebarLayout( sidebarPanel( # Create a text input widget textInput(inputId = "names", label = "Enter Math 241 names here", value = "Kelly"), p("Put single space between the names.") ), mainPanel( plotOutput(outputId = "graph") ) ) ) ``` --- ## UI Controls * All these UI functions are just a way for us to generate HTML (without needing to learn HTML). ```r textInput(inputId = "names", label = "Enter Math 241 names here", value = "Kelly") ```
Enter Math 241 names here
--- ## Bringing it to Life! Two important arguments for the `server()` function: * `input` is a named `list` that controls the input widgets. + EX: `input$names` controls the `textInput()` widget * `output` is a named `list` that controls the objects to display. + EX: `output$graph` ```r server <- function(input, output){ } ``` --- ```r server <- function(input, output){ output$graph <- renderPlot({ dat_names <- babynames %>% group_by(year, name) %>% summarize(n = sum(n)) %>% group_by(year) %>% mutate(prop = n/sum(n)) %>% filter(name %in% c(unlist(str_split(input$names, " "))), year >= 1980) ggplot(data = dat_names, mapping = aes(x = year, y = prop,color = name)) + geom_line(size = 2) }) } ``` --- ## First App! * Let's interact with the app. * Questions? --- ## Let's Add One More Output * Add the following `output$table` to the `server()` function ```r server <- function(input, output){ output$graph <- renderPlot({ ... }) output$table <- renderDataTable({ dat_names <- babynames %>% group_by(year, name) %>% summarize(n = sum(n)) %>% group_by(year) %>% mutate(prop = n/sum(n)) %>% filter(name %in% c(unlist(str_split(input$names, " "))), year >= 1980) dat_names %>% group_by(name) %>% summarize(count = sum(n)) %>% arrange(desc(count)) }) } ``` --- ## Need to Also Place it in the UI ```r # User interface ui <- fluidPage( titlePanel("Which Math 241 name is most popular?"), sidebarLayout( sidebarPanel( # Create a text input widget textInput(inputId = "names", label = "Enter Math 241 names here", value = "Kelly"), p("Put single space between the names.") ), mainPanel( plotOutput(outputId = "graph"), dataTableOutput(outputId = "table") ) ) ) ``` --- ## And to load the `DT` package ```r library(DT) ``` --- ## Reactive Expressions * How is our code redundant? * Let's try to fix it! * Can't create a static data frame. + Need it to be **reactive**! --- ## Reactive Expressions ```r dat_names <- reactive({ babynames %>% group_by(year, name) %>% summarize(n = sum(n)) %>% group_by(year) %>% mutate(prop = n/sum(n)) %>% filter(name %in% c(unlist(str_split(input$names, " "))), year >= 1980) }) ``` --- ## Reactive Expressions * Call reactive expressions like you would a function with no arguments. ```r output$graph <- renderPlot({ ggplot(data = dat_names(), mapping = aes(x = year, y = prop,color = name)) + geom_line(size = 2) }) ``` --- ## Detour: [Clean Up the DataTable](https://shiny.rstudio.com/gallery/datatables-options.html) ```r dat_names_agg <- reactive({ dat_names() %>% group_by(name) %>% summarize(count = sum(n)) %>% arrange(desc(count)) }) output$table <- renderDataTable({ datatable(dat_names_agg(), options = list(paging = FALSE, searching = FALSE, orderClasses = TRUE)) }) ``` --- ## [Input Widgets](https://shiny.rstudio.com/gallery/widget-gallery.html) * Let's practice with: + `radioButtons()` + `sliderInput()` + `submitButton()` + `selectizeInput()` --- ## `radioButtons()` * Within the `ui`: ```r radioButtons(inputId = "variable", label = "Variable of Interest", choices = c("n", "prop"), selected = "prop") ``` * Within the `server()` function: + Must handle the tidy eval issue ```r output$graph <- renderPlot({ ggplot(data = dat_names(), mapping = aes(x = year, y = .data[[input$variable]], color = name)) + geom_line(size = 2) }) ``` --- ## `sliderInput()` * Within the `ui`: ```r # Create slider widget sliderInput("year_range", "Range of Years:", min = min(babynames$year), max = max(babynames$year), value = c(1980,2010)) ``` * Within the `server()` function: ```r dat_names <- reactive({ babynames %>% group_by(year, name) %>% summarize(n = sum(n)) %>% group_by(year) %>% mutate(prop = n/sum(n)) %>% filter(name %in% c(unlist(str_split(input$names, " "))), year >= input$year_range[1], year <= input$year_range[2]) }) ``` --- ## `sliderInput()` * Within the `ui`: ```r sliderInput("year_range", "Range of Years:", min = min(babynames$year), max = max(babynames$year), value = c(1980, 2010), sep = "") ``` --- ## `submitButtion()` * This causes **all** input on the page to not send updates to the server until the button is pressed. ```r submitButton("Update Results!") ``` --- ## `selectizeInput()` * Within the `ui`: ```r selectizeInput(inputId = "names", label = "Enter Math 241 names here", choices = NULL, multiple = TRUE) ``` * Within the `server()` function: ```r server <- function(input, output, session){ updateSelectizeInput(session, 'names', choices = unique(babynames$name), server = TRUE) } ``` --- ## Building Outputs Server-side: * Save the object with `output$___`. * Use a `render___({})` function to create the object. + Look at cheatsheet for examples. * Access inputs with `input$___`. UI-side: * Place output with `___Output()`. --- ## Next Steps **Troubleshooting common issues:** * Must comma separate all the elements in the `ui`. * But don't add a comma after the last element in the `ui`. **Up Next on Thursday**: * Uploading apps to the Reed Shiny Server * Updating `leaflet` maps * Displaying the code in your app * Modifying the layout and adding HTML components * More practice