├── .gitignore ├── figs ├── inputs.png ├── new_app.png ├── outputs.png └── rstudio-hex-shiny-dot-psd.png ├── README.md ├── intro_to_shiny.Rproj ├── apps ├── session_one_app │ └── app.R ├── mortality_report.Rmd └── mortality_dashboard.Rmd ├── shiny_session_one.Rmd ├── shiny_session_two.Rmd ├── shiny_session_three.Rmd ├── shiny_session_one.html ├── shiny_session_two.html └── shiny_session_three.html /.gitignore: -------------------------------------------------------------------------------- 1 | .RHistory 2 | .Rproj.user 3 | *cache 4 | *files 5 | -------------------------------------------------------------------------------- /figs/inputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliasilge/intro_to_shiny/HEAD/figs/inputs.png -------------------------------------------------------------------------------- /figs/new_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliasilge/intro_to_shiny/HEAD/figs/new_app.png -------------------------------------------------------------------------------- /figs/outputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliasilge/intro_to_shiny/HEAD/figs/outputs.png -------------------------------------------------------------------------------- /figs/rstudio-hex-shiny-dot-psd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliasilge/intro_to_shiny/HEAD/figs/rstudio-hex-shiny-dot-psd.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # An Introduction to Shiny 2 | 3 | Some slides and apps for my introductory Shiny workshop at the February 2017 [satRday conference in Cape Town, South Africa](http://satrdays.org/capetown2017/) -------------------------------------------------------------------------------- /intro_to_shiny.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 4 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | -------------------------------------------------------------------------------- /apps/session_one_app/app.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | library(ggplot2) 3 | library(dplyr) 4 | library(DT) 5 | library(southafricastats) 6 | 7 | mortality_zaf <- mortality_zaf %>% 8 | filter(!(indicator %in% c("All causes"))) 9 | 10 | # body of the UI 11 | ui <- fluidPage( 12 | 13 | # application title 14 | titlePanel("Mortality in South African Provinces"), 15 | 16 | # sidebar with input 17 | sidebarLayout( 18 | sidebarPanel( 19 | selectInput(inputId = "province", 20 | label = "Province:", 21 | choices = unique(mortality_zaf$province), 22 | selected = "Gauteng", 23 | multiple = TRUE), 24 | checkboxInput(inputId = "show_data", 25 | label = "Show table?", 26 | value = FALSE) 27 | ), 28 | 29 | # show the output 30 | mainPanel( 31 | plotOutput(outputId = "mortalityPlot"), 32 | dataTableOutput(outputId = "mortalityTable") 33 | ) 34 | ) 35 | ) 36 | 37 | # calculations behind the scenes 38 | server <- function(input, output) { 39 | 40 | selected_df <- reactive({ 41 | mortality_zaf %>% 42 | filter(province %in% input$province) 43 | }) 44 | 45 | output$mortalityPlot <- renderPlot({ 46 | selected_df() %>% 47 | ggplot(aes(year, deaths, color = indicator)) + 48 | geom_line(alpha = 0.8, size = 1.5) + 49 | facet_wrap(~province, scales = "free") + 50 | theme_minimal(base_size = 18) 51 | }) 52 | 53 | output$mortalityTable <- renderDataTable({ 54 | if(input$show_data){ 55 | DT::datatable(data = selected_df()) 56 | } 57 | }) 58 | 59 | } 60 | 61 | # run the application 62 | shinyApp(ui = ui, server = server) 63 | 64 | -------------------------------------------------------------------------------- /apps/mortality_report.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Mortality in South African Provinces" 3 | author: "Julia Silge" 4 | date: "2/5/2017" 5 | output: html_document 6 | runtime: shiny 7 | --- 8 | 9 | ```{r setup, include=FALSE} 10 | knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE) 11 | ``` 12 | 13 | ## Plot without Shiny 14 | 15 | ```{r} 16 | library(dplyr) 17 | library(tidyr) 18 | library(ggplot2) 19 | library(southafricastats) 20 | 21 | totals <- population_zaf %>% 22 | filter(year == 2013) %>% 23 | select(province, total) 24 | 25 | 26 | compare_provinces <- mortality_zaf %>% 27 | left_join(population_zaf) %>% 28 | filter(!is.na(total)) %>% 29 | mutate(mortality = deaths / total * 1e3) %>% 30 | group_by(province, indicator) %>% 31 | summarise(mortality = mean(mortality, na.rm = TRUE)) %>% 32 | ungroup %>% 33 | left_join(totals) %>% 34 | spread(indicator, mortality) 35 | 36 | ``` 37 | 38 | 39 | ```{r, fig.width=8, fig.height=6, echo=FALSE} 40 | ggplot(compare_provinces, aes(`Cerebrovascular diseases (I60-I69)`, 41 | `Diabetes mellitus (E10-E14)`, 42 | size = total, 43 | label = province)) + 44 | geom_point(alpha = 0.7, color = "midnightblue") + 45 | geom_text(aes(size = 3e6), vjust = 2.5) + 46 | theme_minimal() + 47 | theme(legend.position="none") 48 | ``` 49 | 50 | ## Interactive Shiny version of plot 51 | 52 | ```{r, echo = FALSE} 53 | selectInput(inputId = "x", 54 | label = "X-axis:", 55 | choices = colnames(compare_provinces)[3:20], 56 | selected = "Other forms of heart disease (I30-I52)") 57 | 58 | selectInput(inputId = "y", 59 | label = "Y-axis:", 60 | choices = colnames(compare_provinces)[3:20], 61 | selected = "Non-natural causes") 62 | ``` 63 | 64 | ```{r, echo = FALSE, fig.width=8, fig.height=8} 65 | 66 | selected_df <- reactive({ 67 | subset_df <- compare_provinces[, c(1:2, 68 | which(colnames(compare_provinces) == input$x), 69 | which(colnames(compare_provinces) == input$y))] 70 | colnames(subset_df) <- c("province", "total", "selected_x", "selected_y") 71 | subset_df 72 | }) 73 | 74 | fillCol(height = 800, 75 | renderPlot({ 76 | ggplot(selected_df(), aes(x = selected_x, 77 | y = selected_y, 78 | size = total, 79 | label = province)) + 80 | geom_point(alpha = 0.7, color = "midnightblue") + 81 | theme_minimal() + 82 | labs(x = input$x, y = input$y) + 83 | geom_text(aes(size = 1e7), vjust = 2) + 84 | theme_minimal(base_size = 14) + 85 | theme(legend.position="none") 86 | }, height = 800) 87 | ) 88 | ``` 89 | 90 | -------------------------------------------------------------------------------- /apps/mortality_dashboard.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Mortality in South African Provinces" 3 | runtime: shiny 4 | output: 5 | flexdashboard::flex_dashboard: 6 | orientation: columns 7 | vertical_layout: fill 8 | --- 9 | 10 | ```{r setup, include=FALSE} 11 | library(flexdashboard) 12 | library(shiny) 13 | library(dplyr) 14 | library(tidyr) 15 | library(ggplot2) 16 | library(leaflet) 17 | library(southafricastats) 18 | 19 | totals <- population_zaf %>% 20 | filter(year == 2013) %>% 21 | select(province, total) 22 | 23 | compare_provinces <- mortality_zaf %>% 24 | left_join(population_zaf) %>% 25 | filter(!is.na(total)) %>% 26 | mutate(mortality = deaths / total * 1e3) %>% 27 | group_by(province, indicator) %>% 28 | summarise(mortality = mean(mortality, na.rm = TRUE)) %>% 29 | ungroup %>% 30 | left_join(totals) %>% 31 | spread(indicator, mortality) 32 | 33 | mortality_zaf <- mortality_zaf %>% 34 | filter(!(indicator %in% c("All causes"))) 35 | 36 | ``` 37 | 38 | Column {.sidebar} 39 | ===================================================================== 40 | 41 | This app explores mortality data from the [South Africa Data Portal](http://southafrica.opendataforafrica.org/). 42 | 43 | Use the input below to select a cause of death to explore. 44 | 45 | ```{r} 46 | selectInput(inputId = "indicator", 47 | label = "Cause of death:", 48 | choices = unique(mortality_zaf$indicator), 49 | selected = "Tuberculosis (A15-A19)") 50 | ``` 51 | 52 | 53 | 54 | Map {data-icon="fa-map-marker"} 55 | ==================================================================== 56 | 57 | ### Which provinces have a higher mortality rate from the selected cause of death? 58 | 59 | ```{r} 60 | selected_df <- reactive({ 61 | subset_df <- compare_provinces[, c(1, 62 | which(colnames(compare_provinces) == input$indicator))] 63 | colnames(subset_df) <- c("province", "indicator") 64 | population_zaf %>% 65 | filter(year == 2013) %>% 66 | left_join(subset_df, by = "province") %>% 67 | mutate(indicator = indicator / sum(indicator, na.rm = TRUE)) 68 | }) 69 | 70 | renderLeaflet({ 71 | leaflet(selected_df()) %>% 72 | addProviderTiles("CartoDB.Positron") %>% 73 | addCircles(lng = ~longitude, lat = ~latitude, weight = 2.5, 74 | radius = ~sqrt(indicator) * 3e5 , popup = ~province, 75 | color = "magenta") 76 | }) 77 | ``` 78 | 79 | Comparing provinces {data-icon="fa-list"} 80 | ==================================================================== 81 | 82 | ### How does the selected mortality rate compare to the overall mortality rate? 83 | 84 | ```{r} 85 | scatterplot_df <- reactive({ 86 | subset_df <- compare_provinces[, c(1:2, 87 | which(colnames(compare_provinces) == "All causes"), 88 | which(colnames(compare_provinces) == input$indicator))] 89 | colnames(subset_df) <- c("province", "total", "selected_x", "selected_y") 90 | subset_df 91 | }) 92 | 93 | renderPlot({ 94 | ggplot(scatterplot_df(), aes(x = selected_x, 95 | y = selected_y, 96 | size = total, 97 | label = province)) + 98 | geom_point(alpha = 0.7, color = "magenta4") + 99 | theme_minimal() + 100 | labs(x = "All causes", y = input$indicator) + 101 | geom_text(aes(size = 1e7), vjust = 2) + 102 | scale_x_continuous(limits = c(7.7, 14.2)) + 103 | theme_minimal(base_size = 14) + 104 | theme(legend.position="none") 105 | }) 106 | ``` 107 | 108 | Changes in time {data-icon="fa-area-chart"} 109 | ==================================================================== 110 | 111 | ### How have the number of deaths changed in time? 112 | 113 | ```{r} 114 | renderPlot({ 115 | mortality_zaf %>% 116 | filter(indicator == input$indicator) %>% 117 | ggplot(aes(year, deaths, color = province)) + 118 | geom_line(alpha = 0.8, size = 1.5) + 119 | theme_minimal(base_size = 18) + 120 | labs(x = NULL, y = "Number of deaths per year") 121 | }) 122 | ``` 123 | 124 | 125 | Table {data-icon="fa-table"} 126 | ==================================================================== 127 | 128 | ### Explore the data as reported by the South Africa Data Portal 129 | 130 | ```{r} 131 | renderDataTable({ 132 | mortality_zaf %>% 133 | filter(indicator == input$indicator) 134 | }, 135 | options = list(pageLength = 10) 136 | ) 137 | ``` 138 | -------------------------------------------------------------------------------- /shiny_session_one.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "An Introduction to Shiny" 3 | author: "Julia Silge" 4 | output: 5 | rmdshower::shower_presentation: 6 | self_contained: false 7 | highlight: tango 8 | --- 9 | 10 | ```{r, echo = FALSE, warning = FALSE} 11 | library(knitr) 12 | knitr::opts_chunk$set(cache = TRUE, warning = FALSE, message = FALSE, dpi = 180) 13 | options(width=80) 14 | # to knit this document use devtools::install_github("mangothecat/rmdshower") 15 | ``` 16 | 17 | ## An Introduction to Shiny 18 | 19 | 20 | 21 | ### Julia Silge (Stack Overflow) 22 | 23 | #### [\@juliasilge](https://twitter.com/juliasilge) 24 | 25 | #### [http://juliasilge.com/](http://juliasilge.com/) 26 | 27 | 28 | ## Let's install some packages 29 | 30 | ```{r, eval=FALSE} 31 | install.packages(c("shiny", 32 | "dplyr", 33 | "ggplot2", 34 | "DT" 35 | "devtools")) 36 | 37 | devtools::install_github("juliasilge/southafricastats") 38 | ``` 39 | 40 | # What is a Shiny app? 41 | 42 | ## A web application framework for R 43 | 44 | - Shiny allows us as data scientists and analysts to turn our analyses written in R into interactive apps 45 | - A Shiny app is run by a server (computer) 46 | 47 | 48 | 49 | ## Navigation 50 | 51 | - Different viewing modes 52 | - How do you close the app? 53 | - Remember that your computer is running (i.e., serving) the app 54 | - The app is *reactive* 55 | 56 | ## Shiny is flexible {.grid} 57 | 58 | - Check out a [complex example](https://beta.rstudioconnect.com/jcheng/scorecard-app/) 59 | - What is in [the code](https://github.com/jcheng5/scorecard-app)? 60 | 61 | # Inputs and outputs 62 | 63 | ## Reactive programming 64 | 65 | - A Shiny app has an UI and a server function 66 | - The UI controls how the app looks and is laid out 67 | - The server function has the instructions about how to build the app 68 | 69 | ## Reactive programming 70 | 71 | - Most of us who write R code are used to writing scripts to analyze data that are imperative or procedural (or maybe functional) 72 | - A reactive paradigm focuses on how data will flow through the code and how changes propagate 73 | - Reactive programming is important when a user is interacting with an interface 74 | 75 | 76 | ## Inputs {.grid} 77 | 78 | 79 | 80 | 81 | ## Outputs {.grid} 82 | 83 | 84 | 85 | [https://www.rstudio.com/resources/cheatsheets/](https://www.rstudio.com/resources/cheatsheets/) 86 | 87 | ## First steps to reactivity 88 | 89 | Reactivity occurs when an input value is used to render an output object 90 | 91 | ```{r, eval = FALSE} 92 | selectInput(inputId = "province", 93 | label = "Province:", 94 | choices = unique(mortality$province), 95 | selected = "Gauteng") 96 | ``` 97 | 98 | ## First steps to reactivity 99 | 100 | Reactivity occurs when an input value is used to render an output object 101 | 102 | ```{r, eval = FALSE} 103 | # calculations behind the scenes 104 | server <- function(input, output) { 105 | 106 | output$mortalityPlot <- renderPlot({ 107 | mortality %>% 108 | filter(province == input$province) %>% 109 | ggplot(aes(year, deaths, color = indicator)) + 110 | geom_line(alpha = 0.8, size = 1.5) + 111 | theme_minimal(base_size = 18) 112 | }) 113 | } 114 | ``` 115 | 116 | ## First steps to reactivity {.grid} 117 | 118 | Reactivity occurs when an input value is used to render an output object 119 | 120 | - Be sure to add the necessary libraries at the beginning of the app 121 | - Change the name of your output plot 122 | - Do you want to filter out any of the mortality causes? 123 | 124 | ## A new kind of input 125 | 126 | We can select more than one thing at a time 127 | 128 | ```{r, eval = FALSE} 129 | selectInput(inputId = "province", 130 | label = "Province:", 131 | choices = unique(mortality$province), 132 | selected = "Gauteng", 133 | multiple = TRUE) 134 | ``` 135 | 136 | ## A new kind of input 137 | 138 | We can select more than one thing at a time 139 | 140 | ```{r, eval = FALSE} 141 | # calculations behind the scenes 142 | server <- function(input, output) { 143 | 144 | output$mortalityPlot <- renderPlot({ 145 | mortality %>% 146 | filter(province %in% input$province) %>% 147 | ggplot(aes(year, deaths, color = indicator)) + 148 | geom_line(alpha = 0.8, size = 1.5) + 149 | facet_wrap(~province, scales = "free") + 150 | theme_minimal(base_size = 18) 151 | }) 152 | } 153 | ``` 154 | 155 | ## Checkbox input 156 | 157 | ```{r, eval = FALSE} 158 | checkboxInput(inputId = "show_data", 159 | label = "Show table?", 160 | value = FALSE) 161 | ``` 162 | 163 | ## Checkbox input 164 | 165 | ```{r, eval = FALSE} 166 | output$mortalityTable <- renderDataTable({ 167 | if(input$show_data){ 168 | DT::datatable(data = selected_df()) 169 | } 170 | }) 171 | ``` 172 | 173 | # Sharing your app 174 | 175 | ## With someone who has R installed? 176 | 177 | - Share the code for your app (`app.R`) 178 | - Your collaborator can run the app locally 179 | 180 | ## With everyone publicly? 181 | 182 | - Publish using an account on [shinyapps.io](http://www.shinyapps.io/) 183 | - There are a variety of scalable options from free to \$\$\$ 184 | - I started with a free account and now pay for a starter account (after a few of my blog posts with Shiny apps got a lot of views) 185 | 186 | ## With people in your organization? 187 | 188 | - Consider installing Shiny Server on a machine of your own 189 | - It was much easier than I thought 190 | - Check out RStudio's [clear, helpful instructions](https://www.rstudio.com/products/shiny/download-server/) 191 | -------------------------------------------------------------------------------- /shiny_session_two.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "An Introduction to Shiny" 3 | author: "Julia Silge" 4 | output: 5 | rmdshower::shower_presentation: 6 | self_contained: false 7 | highlight: tango 8 | --- 9 | 10 | ```{r, echo = FALSE, warning = FALSE} 11 | library(knitr) 12 | knitr::opts_chunk$set(cache = TRUE, warning = FALSE, message = FALSE, dpi = 180) 13 | options(width=80) 14 | # to knit this document use devtools::install_github("mangothecat/rmdshower") 15 | ``` 16 | 17 | ## An Introduction to Shiny: Session Two 18 | 19 | 20 | 21 | ### Julia Silge (Stack Overflow) 22 | 23 | #### [\@juliasilge](https://twitter.com/juliasilge) 24 | 25 | #### [http://juliasilge.com/](http://juliasilge.com/) 26 | 27 | ## Shiny + R Markdown 28 | 29 | - R Markdown is my jam 30 | - We can use Shiny to make interactive reports with R Markdown 31 | - Add `runtime: shiny` to the YAML of an R Markdown document 32 | 33 | ## Let's install some packages 34 | 35 | ```{r, eval=FALSE} 36 | install.packages(c("shiny", 37 | "dplyr", 38 | "ggplot2", 39 | "tidyr", 40 | "flexdashboard", 41 | "devtools")) 42 | 43 | devtools::install_github("juliasilge/southafricastats") 44 | ``` 45 | 46 | 47 | ## Extending R Markdown with Shiny {.grid} 48 | 49 | - R Markdown has built-in capability to handle Shiny reactivity 50 | - We can write reports that are interactive to the user 51 | - R Markdown reports with Shiny must be run locally or by a Shiny server 52 | 53 | ## Making a regular plot 54 | 55 | ```{r} 56 | library(dplyr) 57 | library(tidyr) 58 | library(ggplot2) 59 | library(southafricastats) 60 | 61 | totals <- population_zaf %>% 62 | filter(year == 2013) %>% 63 | select(province, total) 64 | 65 | 66 | compare_provinces <- mortality_zaf %>% 67 | left_join(population_zaf) %>% 68 | filter(!is.na(total)) %>% 69 | mutate(mortality = deaths / total * 1e3) %>% 70 | group_by(province, indicator) %>% 71 | summarise(mortality = mean(mortality, na.rm = TRUE)) %>% 72 | ungroup %>% 73 | left_join(totals) %>% 74 | spread(indicator, mortality) 75 | ``` 76 | 77 | ## Making a regular plot 78 | 79 | ```{r, eval=FALSE} 80 | ggplot(compare_provinces, aes(`Cerebrovascular diseases (I60-I69)`, 81 | `Diabetes mellitus (E10-E14)`, 82 | size = total, 83 | label = province)) + 84 | geom_point(alpha = 0.7, color = "midnightblue") + 85 | geom_text(aes(size = 3e6), vjust = 2.5) + 86 | theme_minimal() + 87 | theme(legend.position="none") 88 | ``` 89 | 90 | ## Making a regular plot 91 | 92 | ```{r, echo=FALSE, fig.width=4.5, fig.height=3} 93 | ggplot(compare_provinces, aes(`Cerebrovascular diseases (I60-I69)`, 94 | `Diabetes mellitus (E10-E14)`, 95 | size = total, 96 | label = province)) + 97 | geom_point(alpha = 0.7, color = "midnightblue") + 98 | geom_text(aes(size = 1.8e6), vjust = -1.5) + 99 | theme_minimal() + 100 | xlim(c(0.3, 0.7)) + 101 | ylim(c(0.3, 0.55)) + 102 | theme(legend.position="none") 103 | ``` 104 | 105 | ## Making an input for R Markdown 106 | 107 | ```{r, eval = FALSE} 108 | selectInput(inputId = "x", 109 | label = "X-axis:", 110 | choices = colnames(compare_provinces)[3:20], 111 | selected = "Other forms of heart disease (I30-I52)") 112 | 113 | selectInput(inputId = "y", 114 | label = "Y-axis:", 115 | choices = colnames(compare_provinces)[3:20], 116 | selected = "Non-natural causes") 117 | ``` 118 | 119 | 120 | ## What do we do with those strings now? 121 | 122 | - The type of most things you access like `input$x` is a string 123 | - If we didn't have any spaces or special characters, we could use `aes_string` 124 | 125 | ```{r, eval=FALSE} 126 | ggplot(compare_provinces, aes_string(x = input$x, 127 | y = input$y)) + 128 | geom_point() 129 | ``` 130 | 131 | - But alas, we cannot 132 | 133 | 134 | ## What do we do with those strings now? 135 | 136 | - Instead, let's use a `reactive` expression 137 | 138 | ```{r, eval=FALSE} 139 | selected_df <- reactive({ 140 | subset_df <- compare_provinces[, c(1:2, 141 | which(colnames(compare_provinces) == input$x), 142 | which(colnames(compare_provinces) == input$y))] 143 | colnames(subset_df) <- c("province", "total", 144 | "selected_x", "selected_y") 145 | subset_df 146 | }) 147 | ``` 148 | 149 | - A reactive expression uses input from the user and returns a value 150 | - A reactive expression updates every time the input changes 151 | 152 | ## What do we do with those strings now? 153 | 154 | - Instead, let's use a `reactive` expression. 155 | 156 | ```{r, eval=FALSE} 157 | selected_df <- reactive({ 158 | subset_df <- compare_provinces[, c(1:2, 159 | which(colnames(compare_provinces) == input$x), 160 | which(colnames(compare_provinces) == input$y))] 161 | colnames(subset_df) <- c("province", "total", 162 | "selected_x", "selected_y") 163 | subset_df 164 | }) 165 | ``` 166 | 167 | - The best uses for reactive expressions are when you want to avoid re-running unnecessary code 168 | - Only call a `reactive` expression from within another `reactive` function or a `render*` function 169 | 170 | ## Reactive expressions 171 | 172 | - Do not put side effects in reactive expressions 173 | 174 |

.@jcheng on reactive vs observe #shinydevcon #rstats pic.twitter.com/g9hJTbZB1o

— Ajinkya Kale (@ajinkyakale) January 30, 2016
175 | 176 | 177 | ## Writing an output for R Markdown 178 | 179 | ```{r, eval=FALSE} 180 | renderPlot({ 181 | ggplot(selected_df(), aes(x = selected_x, 182 | y = selected_y, 183 | size = total, 184 | label = province)) + 185 | geom_point(alpha = 0.7, color = "midnightblue") + 186 | theme_minimal() + 187 | labs(x = input$x, y = input$y) + 188 | geom_text(aes(size = 1e7), vjust = 2) + 189 | theme_minimal(base_size = 14) + 190 | theme(legend.position="none") 191 | }) 192 | ``` 193 | 194 | # Is that too... squashed?! 195 | 196 | ## Writing an output for R Markdown 197 | 198 | ```{r, eval=FALSE} 199 | fillCol(height = 800, 200 | renderPlot({ 201 | ggplot(selected_df(), aes(x = selected_x, 202 | y = selected_y, 203 | size = total, 204 | label = province)) + 205 | geom_point(alpha = 0.7, color = "midnightblue") + 206 | theme_minimal() + 207 | labs(x = input$x, y = input$y) + 208 | geom_text(aes(size = 1e7), vjust = 2) + 209 | theme_minimal(base_size = 14) + 210 | theme(legend.position="none") 211 | }, height = 800) 212 | ) 213 | ``` 214 | 215 | ## Flexible layouts {.grid} 216 | 217 | - Using [fill layouts](http://shiny.rstudio.com/articles/gadget-ui.html#fillrowfillcol) can be helpful once you are putting Shiny outputs into R Markdown 218 | - Use `fillCol` (or maybe `fillRow` in some situations) to make your report look nice 219 | 220 | ## How to share interactive reports 221 | 222 | - R Markdown reports with `runtime: shiny` must be served by a Shiny server 223 | - Run it locally! Put in on [shinyapps.io](http://www.shinyapps.io/)! Build your own Shiny server! 224 | 225 | ## The flexdashboard package {.grid} 226 | 227 | - I make almost all of my Shiny apps now using the [flexdashboard](http://rmarkdown.rstudio.com/flexdashboard/) package 228 | - The ratio of how good they look to how hard I have to work is *just* right 229 | - Flexdashboard adapts itself for mobile devices 230 | - Flexdashboard does not have to include Shiny elements 231 | 232 | ## Check out a couple of examples {.grid} 233 | 234 | - [Women in the Stack Overflow Developer survey](https://juliasilge.shinyapps.io/survey2016/) 235 | - [Code here](https://github.com/juliasilge/stacksurveyapp) 236 | - [Emergency room visits](https://datassist.shinyapps.io/neiss_demographics/) 237 | - [Code here](https://github.com/juliasilge/neissapp) 238 | -------------------------------------------------------------------------------- /shiny_session_three.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "An Introduction to Shiny" 3 | author: "Julia Silge" 4 | output: 5 | rmdshower::shower_presentation: 6 | self_contained: false 7 | highlight: tango 8 | --- 9 | 10 | ```{r, echo = FALSE, warning = FALSE} 11 | library(knitr) 12 | knitr::opts_chunk$set(cache = TRUE, warning = FALSE, message = FALSE, dpi = 180) 13 | options(width=80) 14 | # to knit this document use devtools::install_github("mangothecat/rmdshower") 15 | ``` 16 | 17 | ## An Introduction to Shiny: Session Three 18 | 19 | 20 | 21 | ### Julia Silge (Stack Overflow) 22 | 23 | #### [\@juliasilge](https://twitter.com/juliasilge) 24 | 25 | #### [http://juliasilge.com/](http://juliasilge.com/) 26 | 27 | ## Let's install some packages 28 | 29 | ```{r, eval=FALSE} 30 | install.packages(c("shiny", 31 | "flexdashboard", 32 | "dplyr", 33 | "tidyr", 34 | "ggplot2", 35 | "leaflet" 36 | "devtools")) 37 | 38 | devtools::install_github("juliasilge/southafricastats") 39 | ``` 40 | 41 | ## Flexdashboards are R Markdown documents {.grid} 42 | 43 | - Most of what we already talked about applies 44 | - It takes care of most annoying layout issues, leaving you with beautiful dashboards without headaches 45 | - Let's check out a [few examples](http://rmarkdown.rstudio.com/flexdashboard/examples.html) 46 | 47 | ## Let's talk about layouts 48 | 49 | - There are many ways to make a dashboard using this package 50 | - RStudio shows lots of [example layouts](http://rmarkdown.rstudio.com/flexdashboard/layouts.html) to get you started 51 | - Flexdashboard uses markdown headers to define different sections of the dashboard. 52 | - You can make dashboards with single or multiple pages, with tabbed columns/rows, with a sidebar, etc. 53 | 54 | # Let's make a multiple page dashboard using Shiny reactivity 55 | 56 | ## Multiple pages {.grid} 57 | 58 | - To make multiple pages in a flexdashboard, use a level 1 markdown header (`=======================`) 59 | - If you have A LOT of pages, you can make menus or use links 60 | - You can use icons in the navigation menu for multiple pages 61 | - One option is [Font Awesome icons](http://fontawesome.io/icons/) 62 | 63 | ## Creating a global sidebar 64 | 65 | - For our dashboard, we want the sidebar to apply across all pages 66 | - We should define the sidebar with a level 1 markdown header 67 | 68 | ```{r, eval = FALSE} 69 | Column {.sidebar} 70 | ===================================================================== 71 | 72 | This app explores mortality data from the 73 | [South Africa Data Portal](http://southafrica.opendataforafrica.org/). 74 | 75 | Use the input below to select a cause of death to explore. 76 | 77 | ``` 78 | 79 | ## Creating a global sidebar 80 | 81 | - Our dashboard is going to use Shiny for reactivity 82 | - Add `runtime: shiny` to the YAML 83 | - Add the Shiny input to a code chunk in the sidebar 84 | 85 | ```{r, eval = FALSE} 86 | selectInput(inputId = "indicator", 87 | label = "Cause of death:", 88 | choices = unique(mortality_zaf$indicator), 89 | selected = "Tuberculosis (A15-A19)") 90 | 91 | ``` 92 | 93 | ## Making our first page 94 | 95 | - Let's make a leaflet map of the selected cause of mortality 96 | - We need the mortality rate `spread` in a new data frame 97 | 98 | ```{r, eval=FALSE} 99 | totals <- population_zaf %>% 100 | filter(year == 2013) %>% 101 | select(province, total) 102 | 103 | compare_provinces <- mortality_zaf %>% 104 | left_join(population_zaf) %>% 105 | filter(!is.na(total)) %>% 106 | mutate(mortality = deaths / total * 1e3) %>% 107 | group_by(province, indicator) %>% 108 | summarise(mortality = mean(mortality, na.rm = TRUE)) %>% 109 | ungroup %>% 110 | left_join(totals) %>% 111 | spread(indicator, mortality) 112 | ``` 113 | 114 | ## Making our first page 115 | 116 | - Now let's set up our first page 117 | 118 | ```{r, eval = FALSE} 119 | Map {data-icon="fa-map-marker"} 120 | ==================================================================== 121 | 122 | ### Which provinces have a higher mortality rate from the selected cause of death? 123 | 124 | ``` 125 | 126 | ## Making our first page 127 | 128 | - Let's set up a `reactive` expression to select the mortality rate we are interested in 129 | 130 | ```{r, eval=FALSE} 131 | selected_df <- reactive({ 132 | subset_df <- compare_provinces[, c(1, 133 | which(colnames(compare_provinces) == input$indicator))] 134 | colnames(subset_df) <- c("province", "indicator") 135 | population_zaf %>% 136 | filter(year == 2013) %>% 137 | left_join(subset_df, by = "province") %>% 138 | mutate(indicator = indicator / sum(indicator, na.rm = TRUE)) 139 | }) 140 | ``` 141 | 142 | ## Making our first page 143 | 144 | - Now we're ready to make a leaflet map 145 | - We can use `renderLeaflet` in the same way we'd use `renderPlot` or another Shiny output 146 | 147 | ```{r, eval=FALSE} 148 | renderLeaflet({ 149 | leaflet(selected_df()) %>% 150 | addProviderTiles("CartoDB.Positron") %>% 151 | addCircles(lng = ~longitude, lat = ~latitude, weight = 2.5, 152 | radius = ~sqrt(indicator) * 3e5 , popup = ~province, 153 | color = "magenta") 154 | }) 155 | ``` 156 | 157 | # Are we ready for another page? 158 | 159 | ## Setting up multiple pages {.grid} 160 | 161 | - We will use the same Shiny input from the global sidebar 162 | - Let's make a scatterplot comparing the selected mortality rate to the overall rate 163 | 164 | ## Making our second page 165 | 166 | ```{r, eval=FALSE} 167 | mortality_zaf <- mortality_zaf %>% 168 | filter(!(indicator %in% c("All causes"))) 169 | ``` 170 | 171 | 172 | ## Making our second page 173 | 174 | ```{r, eval=FALSE} 175 | Comparing provinces {data-icon="fa-list"} 176 | ==================================================================== 177 | 178 | ### How does the selected mortality rate compare to the overall mortality rate? 179 | 180 | ``` 181 | 182 | ## Making our second page 183 | 184 | - Time for another `reactive` expression! 185 | - If we didn't have spaces in our column names here, we could use `aes_string` 186 | 187 | ```{r, eval=FALSE} 188 | scatterplot_df <- reactive({ 189 | subset_df <- compare_provinces[, c(1:2, 190 | which(colnames(compare_provinces) == "All causes"), 191 | which(colnames(compare_provinces) == input$indicator))] 192 | colnames(subset_df) <- c("province", "total", "selected_x", "selected_y") 193 | subset_df 194 | }) 195 | 196 | ``` 197 | 198 | ## Making our second page 199 | 200 | ```{r, eval=FALSE} 201 | renderPlot({ 202 | ggplot(scatterplot_df(), aes(x = selected_x, 203 | y = selected_y, 204 | size = total, 205 | label = province)) + 206 | geom_point(alpha = 0.7, color = "magenta4") + 207 | theme_minimal() + 208 | labs(x = "All causes", y = input$indicator) + 209 | geom_text(aes(size = 1e7), vjust = 2) + 210 | scale_x_continuous(limits = c(7.7, 14.2)) + 211 | theme_minimal(base_size = 14) + 212 | theme(legend.position="none") 213 | }) 214 | ``` 215 | 216 | # Our dashboard is shaping up 217 | 218 | ## Making our third page 219 | 220 | ```{r, eval=FALSE} 221 | Changes in time {data-icon="fa-area-chart"} 222 | ==================================================================== 223 | 224 | ### How have the number of deaths changed in time? 225 | 226 | ``` 227 | 228 | ## Making our third page 229 | 230 | ```{r, eval=FALSE} 231 | renderPlot({ 232 | mortality_zaf %>% 233 | filter(indicator == input$indicator) %>% 234 | ggplot(aes(year, deaths, color = province)) + 235 | geom_line(alpha = 0.8, size = 1.5) + 236 | theme_minimal(base_size = 18) + 237 | labs(x = NULL, y = "Number of deaths per year") 238 | }) 239 | ``` 240 | 241 | # Time for the last page 242 | 243 | ## Making our last page 244 | 245 | ```{r, eval=FALSE} 246 | Table {data-icon="fa-table"} 247 | ==================================================================== 248 | 249 | ### Explore the data as reported by the South Africa Data Portal 250 | 251 | ``` 252 | 253 | ## Making our last page 254 | 255 | ```{r, eval=FALSE} 256 | renderDataTable({ 257 | mortality_zaf %>% 258 | filter(indicator == input$indicator) 259 | }, 260 | options = list(pageLength = 10) 261 | ) 262 | ``` 263 | 264 | ## Options for jazzing up flexdashboards {.grid} 265 | 266 | - Explore the [themes that come in the package](http://rmarkdown.rstudio.com/flexdashboard/using.html#appearance) 267 | - Consider showing the code that made your dashboard with `source_code: embed` 268 | - Consider using plotly or other htmlwidgets 269 | 270 | # Time for you to experiment 271 | -------------------------------------------------------------------------------- /shiny_session_one.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | An Introduction to Shiny 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
58 |

An Introduction to Shiny

59 |

Julia Silge

60 |
61 | 62 | 63 | 64 |
65 |

An Introduction to Shiny

66 |

67 |

Julia Silge (Stack Overflow)

68 |

@juliasilge

69 |

http://juliasilge.com/

70 |
71 |
72 |

Let’s install some packages

73 |
install.packages(c("shiny",
 74 |                    "dplyr",
 75 |                    "ggplot2",
 76 |                    "DT"
 77 |                    "devtools"))
 78 | 
 79 | devtools::install_github("juliasilge/southafricastats")
80 |
81 |

What is a Shiny app?

82 |

A web application framework for R

83 | 87 |

88 |
97 |

Shiny is flexible

98 | 102 |
103 |

Inputs and outputs

104 |

Reactive programming

105 | 110 |
111 |

Reactive programming

112 | 117 |
118 |

Inputs

119 |

120 |
121 |

Outputs

122 |

123 |

https://www.rstudio.com/resources/cheatsheets/

124 |
125 |

First steps to reactivity

126 |

Reactivity occurs when an input value is used to render an output object

127 |
selectInput(inputId = "province",
128 |             label = "Province:",
129 |             choices = unique(mortality$province),
130 |             selected = "Gauteng")
131 |
132 |

First steps to reactivity

133 |

Reactivity occurs when an input value is used to render an output object

134 |
# calculations behind the scenes
135 | server <- function(input, output) {
136 |     
137 |     output$mortalityPlot <- renderPlot({
138 |         mortality %>%
139 |             filter(province == input$province) %>%
140 |             ggplot(aes(year, deaths, color = indicator)) +
141 |             geom_line(alpha = 0.8, size = 1.5) +
142 |             theme_minimal(base_size = 18)
143 |     })
144 | }
145 |
146 |

First steps to reactivity

147 |

Reactivity occurs when an input value is used to render an output object

148 | 153 |
154 |

A new kind of input

155 |

We can select more than one thing at a time

156 |
selectInput(inputId = "province",
157 |             label = "Province:",
158 |             choices = unique(mortality$province),
159 |             selected = "Gauteng",
160 |             multiple = TRUE)
161 |
162 |

A new kind of input

163 |

We can select more than one thing at a time

164 |
# calculations behind the scenes
165 | server <- function(input, output) {
166 |     
167 |     output$mortalityPlot <- renderPlot({
168 |         mortality %>%
169 |             filter(province %in% input$province) %>%
170 |             ggplot(aes(year, deaths, color = indicator)) +
171 |             geom_line(alpha = 0.8, size = 1.5) +
172 |             facet_wrap(~province, scales = "free") +
173 |             theme_minimal(base_size = 18)
174 |     })
175 | }
176 |
177 |

Checkbox input

178 |
checkboxInput(inputId = "show_data",
179 |               label = "Show table?",
180 |               value = FALSE)
181 |
182 |

Checkbox input

183 |
output$mortalityTable <- renderDataTable({
184 |     if(input$show_data){
185 |         DT::datatable(data = selected_df())
186 |     }
187 | })
188 |
189 |

Sharing your app

190 |

With someone who has R installed?

191 | 195 |
196 |

With everyone publicly?

197 | 202 |
203 |

With people in your organization?

204 | 209 |
210 | 211 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | -------------------------------------------------------------------------------- /shiny_session_two.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | An Introduction to Shiny 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
58 |

An Introduction to Shiny

59 |

Julia Silge

60 |
61 | 62 | 63 | 64 |
65 |

An Introduction to Shiny: Session Two

66 |

67 |

Julia Silge (Stack Overflow)

68 |

@juliasilge

69 |

http://juliasilge.com/

70 |
71 |
72 |

Shiny + R Markdown

73 | 78 |
79 |
80 |

Let’s install some packages

81 |
install.packages(c("shiny",
 82 |                    "dplyr",
 83 |                    "ggplot2",
 84 |                    "tidyr",
 85 |                    "flexdashboard",
 86 |                    "devtools"))
 87 | 
 88 | devtools::install_github("juliasilge/southafricastats")
89 |
90 |
91 |

Extending R Markdown with Shiny

92 | 97 |
98 |
99 |

Making a regular plot

100 |
library(dplyr)
101 | library(tidyr)
102 | library(ggplot2)
103 | library(southafricastats)
104 | 
105 | totals <- population_zaf %>% 
106 |     filter(year == 2013) %>% 
107 |     select(province, total)
108 | 
109 | 
110 | compare_provinces <- mortality_zaf %>%
111 |     left_join(population_zaf) %>%
112 |     filter(!is.na(total)) %>%
113 |     mutate(mortality = deaths / total * 1e3) %>%
114 |     group_by(province, indicator) %>%
115 |     summarise(mortality = mean(mortality, na.rm = TRUE)) %>%
116 |     ungroup %>%
117 |     left_join(totals) %>%
118 |     spread(indicator, mortality)
119 |
120 |
121 |

Making a regular plot

122 |
ggplot(compare_provinces, aes(`Cerebrovascular diseases (I60-I69)`,
123 |                               `Diabetes mellitus (E10-E14)`,
124 |                               size = total,
125 |                               label = province)) +
126 |     geom_point(alpha = 0.7, color = "midnightblue") +
127 |     geom_text(aes(size = 3e6), vjust = 2.5) +
128 |     theme_minimal() +
129 |     theme(legend.position="none")
130 |
131 |
132 |

Making a regular plot

133 |

134 |
135 |
136 |

Making an input for R Markdown

137 |
selectInput(inputId = "x", 
138 |             label = "X-axis:",
139 |             choices = colnames(compare_provinces)[3:20], 
140 |             selected = "Other forms of heart disease (I30-I52)")
141 | 
142 | selectInput(inputId = "y", 
143 |             label = "Y-axis:",
144 |             choices = colnames(compare_provinces)[3:20], 
145 |             selected = "Non-natural causes")
146 |
147 |
148 |

What do we do with those strings now?

149 | 153 |
ggplot(compare_provinces, aes_string(x = input$x, 
154 |                                      y = input$y)) +
155 |     geom_point()
156 | 159 |
160 |
161 |

What do we do with those strings now?

162 | 165 |
selected_df <- reactive({
166 |     subset_df <- compare_provinces[, c(1:2, 
167 |                                      which(colnames(compare_provinces) == input$x),
168 |                                      which(colnames(compare_provinces) == input$y))]
169 |     colnames(subset_df) <- c("province", "total", 
170 |                              "selected_x", "selected_y")
171 |     subset_df
172 | })
173 | 177 |
178 |
179 |

What do we do with those strings now?

180 | 183 |
selected_df <- reactive({
184 |     subset_df <- compare_provinces[, c(1:2, 
185 |                                      which(colnames(compare_provinces) == input$x),
186 |                                      which(colnames(compare_provinces) == input$y))]
187 |     colnames(subset_df) <- c("province", "total", 
188 |                              "selected_x", "selected_y")
189 |     subset_df
190 | })
191 | 195 |
196 |
197 |

Reactive expressions

198 | 201 |
202 |

203 | .@jcheng on reactive vs observe #shinydevcon #rstats pic.twitter.com/g9hJTbZB1o 204 |

205 | — Ajinkya Kale (@ajinkyakale) January 30, 2016 206 |
207 | 208 |
209 |
210 |

Writing an output for R Markdown

211 |
renderPlot({
212 |     ggplot(selected_df(), aes(x = selected_x,
213 |                               y = selected_y,
214 |                               size = total,
215 |                               label = province)) +
216 |         geom_point(alpha = 0.7, color = "midnightblue") +
217 |         theme_minimal() +
218 |         labs(x = input$x, y = input$y) +
219 |         geom_text(aes(size = 1e7), vjust = 2) +
220 |         theme_minimal(base_size = 14) +
221 |         theme(legend.position="none")
222 | })
223 |
224 |

Is that too… squashed?!

225 |

Writing an output for R Markdown

226 |
fillCol(height = 800, 
227 |         renderPlot({
228 |             ggplot(selected_df(), aes(x = selected_x,
229 |                                       y = selected_y,
230 |                                       size = total,
231 |                                       label = province)) +
232 |                 geom_point(alpha = 0.7, color = "midnightblue") +
233 |                 theme_minimal() +
234 |                 labs(x = input$x, y = input$y) +
235 |                 geom_text(aes(size = 1e7), vjust = 2) +
236 |                 theme_minimal(base_size = 14) +
237 |                 theme(legend.position="none")
238 |         }, height = 800)
239 | )
240 |
241 |

Flexible layouts

242 | 246 |
247 |

How to share interactive reports

248 | 252 |
253 |

The flexdashboard package

254 | 260 |
261 |

Check out a couple of examples

262 | 268 |
269 | 270 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | -------------------------------------------------------------------------------- /shiny_session_three.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | An Introduction to Shiny 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
58 |

An Introduction to Shiny

59 |

Julia Silge

60 |
61 | 62 | 63 | 64 |
65 |

An Introduction to Shiny: Session Three

66 |

67 |

Julia Silge (Stack Overflow)

68 |

@juliasilge

69 |

http://juliasilge.com/

70 |
71 |
72 |

Let’s install some packages

73 |
install.packages(c("shiny",
 74 |                    "flexdashboard",
 75 |                    "dplyr",
 76 |                    "tidyr",
 77 |                    "ggplot2",
 78 |                    "leaflet"
 79 |                    "devtools"))
 80 | 
 81 | devtools::install_github("juliasilge/southafricastats")
82 |
83 |
84 |

Flexdashboards are R Markdown documents

85 | 90 |
91 |
92 |

Let’s talk about layouts

93 | 99 |
100 |

Let’s make a multiple page dashboard using Shiny reactivity

101 |

Multiple pages

102 | 108 |
109 |

Creating a global sidebar

110 | 114 |
Column {.sidebar}
115 | =====================================================================
116 | 
117 | This app explores mortality data from the 
118 | [South Africa Data Portal](http://southafrica.opendataforafrica.org/).
119 | 
120 | Use the input below to select a cause of death to explore.
121 |
122 |

Creating a global sidebar

123 | 128 |
selectInput(inputId = "indicator",
129 |             label = "Cause of death:",
130 |             choices = unique(mortality_zaf$indicator),
131 |             selected = "Tuberculosis (A15-A19)")
132 |
133 |

Making our first page

134 | 138 |
totals <- population_zaf %>% 
139 |     filter(year == 2013) %>% 
140 |     select(province, total)
141 | 
142 | compare_provinces <- mortality_zaf %>%
143 |     left_join(population_zaf) %>%
144 |     filter(!is.na(total)) %>%
145 |     mutate(mortality = deaths / total * 1e3) %>%
146 |     group_by(province, indicator) %>%
147 |     summarise(mortality = mean(mortality, na.rm = TRUE)) %>%
148 |     ungroup %>%
149 |     left_join(totals) %>%
150 |     spread(indicator, mortality)
151 |
152 |

Making our first page

153 | 156 |
Map {data-icon="fa-map-marker"}
157 | ====================================================================
158 | 
159 | ### Which provinces have a higher mortality rate from the selected cause of death?
160 |
161 |

Making our first page

162 | 165 |
selected_df <- reactive({
166 |     subset_df <- compare_provinces[, c(1, 
167 |                                        which(colnames(compare_provinces) == input$indicator))]
168 |     colnames(subset_df) <- c("province", "indicator")
169 |     population_zaf %>%
170 |         filter(year == 2013) %>%
171 |         left_join(subset_df, by = "province") %>%
172 |         mutate(indicator = indicator / sum(indicator, na.rm = TRUE))
173 | })
174 |
175 |

Making our first page

176 | 180 |
renderLeaflet({
181 |     leaflet(selected_df()) %>%
182 |         addProviderTiles("CartoDB.Positron") %>%
183 |         addCircles(lng = ~longitude, lat = ~latitude, weight = 2.5,
184 |              radius = ~sqrt(indicator) * 3e5 , popup = ~province,
185 |              color = "magenta")
186 | })
187 |
188 |

Are we ready for another page?

189 |

Setting up multiple pages

190 | 194 |
195 |

Making our second page

196 |
mortality_zaf <- mortality_zaf %>%
197 |     filter(!(indicator %in% c("All causes")))
198 |
199 |

Making our second page

200 |
Comparing provinces {data-icon="fa-list"}
201 | ====================================================================
202 | 
203 | ### How does the selected mortality rate compare to the overall mortality rate?
204 |
205 |

Making our second page

206 | 210 |
scatterplot_df <- reactive({
211 |     subset_df <- compare_provinces[, c(1:2, 
212 |                                        which(colnames(compare_provinces) == "All causes"),
213 |                                        which(colnames(compare_provinces) == input$indicator))]
214 |     colnames(subset_df) <- c("province", "total", "selected_x", "selected_y")
215 |     subset_df
216 | })
217 |
218 |

Making our second page

219 |
renderPlot({
220 |     ggplot(scatterplot_df(), aes(x = selected_x,
221 |                                  y = selected_y,
222 |                                  size = total,
223 |                                  label = province)) +
224 |         geom_point(alpha = 0.7, color = "magenta4") +
225 |         theme_minimal() +
226 |         labs(x = "All causes", y = input$indicator) +
227 |         geom_text(aes(size = 1e7), vjust = 2) +
228 |         scale_x_continuous(limits = c(7.7, 14.2)) +
229 |         theme_minimal(base_size = 14) +
230 |         theme(legend.position="none")
231 | })
232 |
233 |

Our dashboard is shaping up

234 |

Making our third page

235 |
Changes in time {data-icon="fa-area-chart"}
236 | ====================================================================
237 | 
238 | ### How have the number of deaths changed in time?
239 |
240 |

Making our third page

241 |
renderPlot({
242 |     mortality_zaf %>%
243 |         filter(indicator == input$indicator) %>%
244 |         ggplot(aes(year, deaths, color = province)) +
245 |         geom_line(alpha = 0.8, size = 1.5) +
246 |         theme_minimal(base_size = 18) +
247 |         labs(x = NULL, y = "Number of deaths per year")
248 | })
249 |
250 |

Time for the last page

251 |

Making our last page

252 |
Table {data-icon="fa-table"}
253 | ====================================================================
254 | 
255 | ### Explore the data as reported by the South Africa Data Portal
256 |
257 |

Making our last page

258 |
renderDataTable({
259 |     mortality_zaf %>%
260 |         filter(indicator == input$indicator)
261 |     },
262 |     options = list(pageLength = 10)
263 |     )
264 |
265 |

Options for jazzing up flexdashboards

266 | 271 |
272 |

Time for you to experiment

273 | 274 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | --------------------------------------------------------------------------------