- Week 6: Introduction to Shiny
- Week 7: Building Interactive UI Components
- Week 8: Linking
ggplot2with Shiny for Dynamic Visualisation - Week 9: Creating and Deploying Dashboards
ggplot2 with Shiny for Dynamic VisualisationBy the end of this session (and your own reading), you should be able to \(\dots\)
Download exercises from Week 6 folder on NOW and move them into your R-project directory.
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
Common Elements
Multi-language written picture-naming dataset app of the data published in Torrance et al. (2018).
Non-WEIRD-WEB app published in Garcia et al. (2022).
Other dashboards:
Strategic Decisions
Operational Decisions
Financial Decisions
Marketing Decisions
HR and Talent Decisions
Academic or Research Decisions
Too Much Data
Poor Visual Design
Unclear Metrics
Lack of Interactivity
Redundant or Irrelevant Information
Clear Purpose
Prioritized Information
Simple and Intuitive Layout
Interactive Features
Contextual Insights
Real-Time Data
# Install shiny
install.packages("shiny")
# Load shiny
library(shiny)
Short cuts
shinyapp (from snippet package)File > New File > Shiny Web App...fluidPage, sidebarLayout)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)
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:
Esc.Workflow of Shiny app development
Cmd/Ctrl + Shift + Enter.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).
sliderInputinput$...output$...renderTexttextOutput{...}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$...renderTexttextOutput{...}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$...renderTexttextOutput{...}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$...renderTexttextOutput{...}1_app.RtextInput("text", "Write some text")input$text to paste(---, input$num)
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.”
Traditional vs Reactive R
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)
})
}| 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.). |
renderUI() + uiOutput() is special because it lets you create dynamic UI elements.2a_app for renderPlot() + plotOutput().Imagine this flow:
input$slider)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.
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()”.
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({...})var <- reactive({...})var()reactlog::reactlog_enable(): Enables a visual debugger for the reactive graph.Ctrl + F3 (or Cmd + F3 on Mac) to open the reactive graph viewer (you might need the function key Fn).1_app.R and then compare to 2b_app.R.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.
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.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.observeEvent()observe() when you only care about one trigger.observeEvent(input$goButton, {
showNotification("Button clicked!")
})
Only runs when input$goButton changes (e.g., is clicked).
observe() to print the value of a slider every time it changes (see 4_app.R).observeEvent() to show a notification when a button is clicked (see 5_app.R).observeEvent() to update a plot only when a button is pressed, not when inputs change.reactiveVal() (compare 6a_app.R to 6b_app.R)eventReactive() (see 7_app.R)Let’s level up with a more challenging Shiny app that combines:
8_app.R)Combining a few of these tools:
reactive() for dynamic datarenderUI() for conditional UIeventReactive() controls updatesobserveEvent() handles side effectsreactiveVal() for state trackingreq() (check required values) to avoid errors from missing inputs8_app.R that allows the user to change the number of bins used by the histogram.reactlog::reactlog_enable() to debugreq() to avoid errors from missing inputsAndrews, 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.