General outline for weeks 6 – 9

  • Week 6: Introduction to Shiny
  • Week 7: Building Interactive UI Components
  • Week 8: Linking ggplot2 with Shiny for Dynamic Visualisation
  • Week 9: Creating and Deploying Dashboards

Objectives for today

By the end of this session (and your own reading), you should be able to \(\dots\)

  1. describe what data dashboards are,
  2. understand the anatomy of Shiny Apps,
  3. understand and employ basic reactive programming.

Download exercises from Week 6 folder on NOW and move them into your R-project directory.

What are interactive data dashboards?

Dashboards are interactive tools for data communication and decision-making.

A data dashboard is a visual interface that displays key information, metrics, and insights from data, often in real time, using charts, tables, and interactive controls.

Purpose

  • Summarise complex data in a digestible format
  • Enable exploration and decision-making
  • Provide interactivity for filtering, drilling down, and customising views

Common Elements

  • Inputs: filters, selectors, sliders
  • Outputs: plots, tables, maps
  • Layout: organised panels or tabs
  • Reactivity: updates based on user interaction

What are interactive data dashboards?

What kinds of decisions can dashboards support?

Strategic Decisions

  • Business growth planning: Identifying high-performing regions or products.
  • Resource allocation: Deciding where to invest time, money, or personnel.
  • Market expansion: Evaluating potential new markets based on performance data.

Operational Decisions

  • Daily performance monitoring: Tracking customer service metrics, or production rates.
  • Inventory management: Deciding when to reorder stock or adjust supply chain logistics.
  • Staff scheduling: Adjusting shifts based on workload and performance data.

Financial Decisions

  • Budgeting and forecasting: Comparing actual vs. projected expenses and revenues.
  • Cost control: Identifying areas of overspending or inefficiency.
  • Profitability analysis: Evaluating which products or services yield the highest margins.

Marketing Decisions

  • Campaign performance: Assessing ROI of marketing efforts.
  • Customer segmentation: Targeting specific groups based on behavior or demographics.
  • Channel optimization: Deciding which platforms or strategies are most effective.

HR and Talent Decisions

  • Hiring needs: Monitoring turnover rates and workforce gaps.
  • Employee performance: Identifying top performers or areas needing support.
  • Training effectiveness: Evaluating the impact of development programs.

Academic or Research Decisions

  • Student performance tracking: Identifying trends across classes or schools.
  • Experiment monitoring: Visualising data from ongoing studies.
  • Resource usage: Deciding how to allocate lab time, equipment, or funding.

What Makes a Dashboard Overwhelming

Too Much Data

  • Trying to show everything at once leads to cognitive overload.
  • No clear focus or prioritization.

Poor Visual Design

  • Clashing colors, inconsistent chart types, or excessive use of gauges and pie charts.
  • Lack of whitespace or visual separation.


Unclear Metrics

  • Ambiguous labels or unfamiliar acronyms.
  • No explanation of what the numbers mean or why they matter.

Lack of Interactivity

  • Static dashboards that don’t allow users to explore or customize views.

Redundant or Irrelevant Information

  • Including metrics that don’t support any actionable decision.

What Makes a Dashboard Useful

Clear Purpose

  • Focused on specific goals or decisions (e.g. student performance, monitoring sales).
  • Tailored to the needs of the user or team.

Prioritized Information

  • Highlights the most important metrics at the top.
  • Uses visual hierarchy to guide attention.

Simple and Intuitive Layout

  • Clean design with logical grouping of elements.
  • Minimal use of colors and charts only where they add clarity.

Interactive Features

  • Filters, drill-downs, and sliders allow users to explore data without cluttering the view.

Contextual Insights

  • Includes benchmarks, comparisons, or alerts to help interpret data.

Real-Time Data

  • Ensures decisions are based on current and relevant information.

What is Shiny?

  • A web application framework for R
  • Enables interactive data exploration
  • Built on reactive programming principles
# Install shiny
install.packages("shiny")
# Load shiny
library(shiny)

Short cuts

  • Type shinyapp (from snippet package)
  • From menu: File > New File > Shiny Web App...
  • Click on “Run App”

App Components

  • Inputs (filters)
  • Outputs (plots/tables)
  • Layout (fluidPage, sidebarLayout)
  • Reactivity (how inputs affect outputs)

Anatomy of a Shiny App (see 0_app.R)

library(shiny)
ui <- fluidPage( # layout function that sets up the basic visual structure of the page.
  # User interface / "frontend": what do you want the user to see / do?
  # This is how the interface should look like and
  # what information the user should be allowed to provide.
  # Input controls lets the user interact with the app by providing values.
)

server <- function(input, output, session) {
  # "Backend": what do you want the app to do?
  # This is the heart of the app: each app should have a purpose. 
}
shinyApp(ui, server)

Anatomy of a Shiny App

In the R console you’ll see something like:

Listening on http://127.0.0.1:3996

This tells you the URL where your app can be found: 127.0.0.1 is a standard address that means “this computer” and 3996 is a randomly assigned port number. You can enter that URL into any compatible web browser to open another copy of your app.

Stopping the app:

  • Click the stop sign icon on the R console toolbar.
  • Click on the console, then press Esc.
  • Close the Shiny app window.

Anatomy of a Shiny App

Workflow of Shiny app development

  • Write some code, start the app, play with the app, write some more code, and repeat.
  • You don’t even need to stop and re-start: press the Reload app button in the toolbox or use the Cmd/Ctrl + Shift + Enter.

Anatomy of a Shiny App

library(shiny)

ui <- fluidPage(
  sliderInput("num", "Choose a number", 1, 100, 50),

)

server <- function(input, output) {

  
  
}

shinyApp(ui, server)

Simple app that prints numbers (see 1_app.R).

  • sliderInput
  • input$...
  • output$...
  • renderText
  • textOutput
  • Use of {...}

Anatomy of a Shiny App

library(shiny)

ui <- fluidPage(
  sliderInput("num", "Choose a number", 1, 100, 50),
  
)

server <- function(input, output) {
  output$result <- renderText({
    
  })
}

shinyApp(ui, server)

Simple app that prints numbers (see 1_app.R).

  • sliderInput:
  • input$...
  • output$...
  • renderText
  • textOutput
  • Use of {...}

Anatomy of a Shiny App

library(shiny)

ui <- fluidPage(
  sliderInput("num", "Choose a number", 1, 100, 50),
  
)

server <- function(input, output) {
  output$result <- renderText({
    paste("You chose", input$num)
  })
}

shinyApp(ui, server)

Simple app that prints numbers (see 1_app.R).

  • sliderInput:
  • input$...
  • output$...
  • renderText
  • textOutput
  • Use of {...}

Anatomy of a Shiny App

library(shiny)

ui <- fluidPage(
  sliderInput("num", "Choose a number", 1, 100, 50),
  textOutput("result")
)

server <- function(input, output) {
  output$result <- renderText({
    paste("You chose", input$num)
  })
}

shinyApp(ui, server)

Simple app that prints numbers (see 1_app.R).

  • sliderInput:
  • input$...
  • output$...
  • renderText
  • textOutput
  • Use of {...}

Exercise 1: Modify the App 1_app.R

  • Add a second input: textInput("text", "Write some text")
  • Display both inputs in the output by adding

input$text to paste(---, input$num)

What is Reactive Programming?

From Wickham (2021):

“[It’s] telling Shiny how to perform a computation, not ordering Shiny to actually go do it. It’s like the difference between giving someone a recipe versus demanding that they go make you a sandwich.”

What is Reactive Programming?

  • Focused on data flows and the propagation of change.
  • When a user interacts with an input (like a slider or text box), any outputs that depend on that input automatically update.

Traditional vs Reactive R

  • Traditional R: You run a script top-to-bottom. If you want to change something, you re-run the script.
  • Reactive R (Shiny): You define relationships between inputs and outputs. When an input changes, the output updates automatically.

Key Reactive Components in Shiny

  • input$... Represents user input (e.g., from sliders, text boxes).
  • render*() Creates reactive outputs (e.g., renderPlot, renderText).
  • reactive() Defines reactive expressions (intermediate computations).
  • observe() Executes code in response to changes, but doesn’t return a value.
  • observeEvent() Responds to specific events (e.g., button clicks).
# User input
ui <- fluidPage(
  sliderInput("num", 
              "Choose a number", 
              min = 1, 
              max = 100, 
              value = 50),
  textOutput("result")
)

# Server backend
server <- function(input, output) {
  output$result <- renderText({
    paste("You chose", input$num)
  })
}

Rendering and output functions

Render Function (server) UI Output Function Purpose
renderText() textOutput() Displays plain text generated on the server.
renderPrint() verbatimTextOutput() Displays printed R output (e.g., summary(), str()) in fixed-width font.
renderTable() tableOutput() Displays a static HTML table of data.
renderDataTable() dataTableOutput() Displays an interactive table using DT (sortable, searchable).
renderPlot() plotOutput() Displays a static plot generated by R graphics (e.g., hist(), ggplot()).
renderImage() imageOutput() Displays an image file (e.g., PNG, JPEG).
renderUI() uiOutput() Dynamically generates UI elements on the server side.
renderText() + HTML htmlOutput() Displays HTML content (formatted text, links, etc.).

Rendering and output functions

  • Render functions live in the server function and create reactive outputs.
  • UI output functions live in the UI and act as placeholders for those outputs.
  • renderUI() + uiOutput() is special because it lets you create dynamic UI elements.
  • See 2a_app for renderPlot() + plotOutput().

What is a Reactive Graph?

Imagine this flow:

  1. User changes a slider (input$slider)
  2. A reactive expression recalculates based on that input.
  3. A render function updates the output (e.g., a plot or text).

This chain of dependencies is what we call the reactive graph.

The reactive graph is a conceptual diagram showing how different parts of your Shiny app depend on each other.

Each node is a reactive object (input, reactive expression, output), and edges show dependencies.

What is a Reactive Graph?

ui <- fluidPage(
  sliderInput("n", "Sample size", 10, 100, 50),
  plotOutput("hist")
)

server <- function(input, output) {
  data <- reactive({
    rnorm(input$n)
  })

  output$hist <- renderPlot({
    histogram(data = NULL, x = data())
  })
}


Reactive graph:

input$n ──▶ data() ──▶ output$hist

  • input$n is the source.
  • data() is a reactive expression that depends on input$n.
  • output$hist depends on data().

If input$n changes, data() recalculates, and output$hist re-renders.

Btw, notice the parentheses for “data()”.

What is a Reactive Graph?

ui <- fluidPage(
  sliderInput("n", "Sample size", 10, 100, 50),
  plotOutput("hist")
)

server <- function(input, output) {
  data <- reactive({
    rnorm(input$n)
  })

  output$hist <- renderPlot({
    histogram(data = NULL, x = data())
  })
}


  • Create a reactive expression by wrapping a block of code in reactive({...})
  • Assign it to a variable: var <- reactive({...})
  • Use a reactive expression by calling the variable like a function: e.g. var()
  • The reactive expression (unlike a function) runs the first time it’s called; then it caches its result until it needs to be updated.

Exercise 2: Tools to Visualize Reactivity

  • reactlog::reactlog_enable(): Enables a visual debugger for the reactive graph.
  • After running your app, press Ctrl + F3 (or Cmd + F3 on Mac) to open the reactive graph viewer (you might need the function key Fn).
  • Check 1_app.R and then compare to 2b_app.R.

Exercise 3

Let’s explore how a reactivity graph for multiple inputs looks like using 3_app.R.

In the app rnorm() is used to create normal distributed random data.

# Example for 1000 normal distributed random data with a mean of 50 and
# a standard deviation of 10
rnorm(n = 1000, mean = 50, sd = 10)

Allow the user to define the mean and the SD via input sliders.

Understanding observe() and observeEvent()

  • observe() and observeEvent() are not part of the reactive graph.
  • observe() and observeEvent() trigger “side effects” or respond to events without producing reactive values.
  • Side effects don’t produce a reactive value but still causes a change: printing to the console, logging, saving data, updating the UI, saving a file, or showing a notification.

Observers: observe()

Runs code in response to reactive changes but don’t return value.

observe({
  print(input$slider)
})

This runs every time input$slider changes, but doesn’t affect other reactive expressions.

  • reactive() is like a formula in a spreadsheet.
  • observeEvent() is like a macro that runs when you click a button.

Observers: observeEvent()

  • Responds to a specific event like a button click.
  • More efficient than observe() when you only care about one trigger.
observeEvent(input$goButton, {
  showNotification("Button clicked!")
})

Only runs when input$goButton changes (e.g., is clicked).

Exercises

  1. Logging Input Changes: Use observe() to print the value of a slider every time it changes (see 4_app.R).
  2. Button Triggered Action: Use observeEvent() to show a notification when a button is clicked (see 5_app.R).
  3. Conditional UI Update: Use observeEvent() to update a plot only when a button is pressed, not when inputs change.
  • Store and update a value manually: reactiveVal() (compare 6a_app.R to 6b_app.R)
  • Compute a value only when an event occurs: eventReactive() (see 7_app.R)

Combining reactive expressions and observers

Let’s level up with a more challenging Shiny app that combines:

  • Multiple reactive expressions
  • Observers for side effects
  • Conditional UI updates
  • A bit of state management

Interactive Data Explorer with Controlled Updates (8_app.R)

Combining a few of these tools:

  • reactive() for dynamic data
  • renderUI() for conditional UI
  • eventReactive() controls updates
  • observeEvent() handles side effects
  • reactiveVal() for state tracking
  • req() (check required values) to avoid errors from missing inputs

Extending the App!

  • Add an option to 8_app.R that allows the user to change the number of bins used by the histogram.
  • Add an option that allows the user to define their own text for the x-axis.

Tips

  • Use reactlog::reactlog_enable() to debug
  • Always test small components first
  • Use req() to avoid errors from missing inputs
  • Shiny assistant
  • Shiny cheatsheet

Recommended reading

References

Andrews, M. (2021). Doing data science in R: An introduction for Social Scientists. SAGE Publications Ltd.

Garcia, R., Roeser, J., & Kidd, E. (2022). Online data collection to address language sampling bias: Lessons from the COVID-19 pandemic. Linguistics Vanguard. https://doi.org/10.1515/lingvan-2021-0040

Torrance, M., Nottbusch, G., Alves, R. A., Arfé, B., Chanquoy, L., Chukharev-Hudilainen, E., Dimakos, I., Fidalgo, R., Hyönä, J., Jóhannesson, Ó. I., et al. (2018). Timed written picture naming in 14 European languages. Behavior Research Methods, 50(2), 744–758.

Wickham, H. (2021). Mastering shiny. O’Reilly Media.