library(dplyr)
library(ggplot2)
library(plotly)
library(leaflet)
library(sf)
library(spData) # provides the `world` mapPractical #11
Advanced Statistical Programming using R — Interactive Data Storytelling
Quiz
Before starting, work through this QUIZ to check your understanding of this week’s lecture on interactive and geospatial visualisation.
Overview
Yesterday you saw how to turn a static figure into something a reader can explore — ggplotly() and plotly, leaflet maps, and geospatial plots with geom_sf() and {geofacet}. Today you try them out, then bring one interactive view into your own project.
Part 1 is a short sandbox on palmerpenguins and the built-in North Carolina shapefile, with solutions. Part 2 is the real thing: make one plot from your project interactive and commit it.
Budget roughly 2 hours: about 40 minutes on the sandbox and the rest on your own project.
You will need plotly, leaflet, sf and spData. Install them inside your project (so renv records them) and snapshot afterwards:
install.packages(c("plotly", "leaflet", "sf", "spData"))
renv::snapshot() # run in the R consolePart 1: Sandbox
~40 min
Exercise 1.1: Make a ggplot interactive
mpg (built into {ggplot2}) records fuel economy for 234 cars. Here is a static scatter — engine size against highway mileage. Wrap it in ggplotly() so readers can hover, zoom, and toggle classes in the legend.
ggplot(mpg, aes(displ, hwy, colour = class)) +
geom_point(alpha = 0.7) +
labs(x = "Engine displacement (L)", y = "Highway mpg", colour = "Class") +
theme_minimal()
Then add the car model to the hover tooltip without drawing it on the plot, and restrict the tooltip to the fields you care about.
p <- ggplot(mpg, aes(displ, hwy, colour = class,
text = paste(manufacturer, model))) +
geom_point(alpha = 0.7) +
labs(x = "Engine displacement (L)", y = "Highway mpg", colour = "Class") +
theme_minimal()
ggplotly(p, tooltip = c("text", "x", "y", "colour"))The text aesthetic is never rendered in the static plot — ggplotly() only uses it for the tooltip. tooltip = controls which fields show on hover; without it, every aesthetic appears. Click a class in the legend to hide it, double-click to isolate.
Exercise 1.2: A geom_sf() choropleth
{spData} ships world, an sf object of country boundaries with a few indicators. Map lifeExp (life expectancy) to fill colour.
data(world)ggplot(world) +
geom_sf(aes(fill = lifeExp)) +
scale_fill_viridis_c(name = "Life\nexpectancy") +
labs(title = "Life expectancy by country") +
theme_void()
An sf object is just a data frame with a geometry column, so aes(fill = ...) works like any other chart. theme_void() drops axes and grid — right for maps. geom_sf() handles the projection for you.
Exercise 1.3: The same map, interactive
Turn the choropleth into a leaflet map: a background layer, country polygons filled by lifeExp, a country-name label on hover, and a legend.
pal <- colorNumeric("viridis", domain = world$lifeExp)
leaflet(world) |>
addTiles() |>
addPolygons(fillColor = ~pal(lifeExp), fillOpacity = 0.7,
weight = 1, color = "white", label = ~name_long) |>
addLegend(pal = pal, values = ~lifeExp,
title = "Life expectancy", position = "bottomright")Build the map layer by layer: addTiles() for the background, addPolygons() for the filled shapes, addLegend() for the colour key. colorNumeric() is reused by both the polygons and the legend so they stay in sync. leaflet reprojects the sf object to WGS84 automatically.
Part 2: Your project — one interactive view
~70 min
By the end you should have one interactive plot committed to your project repo.
Exercise 2.1: Turn an existing plot interactive
Pick a ggplot2 figure you already have and wrap it in ggplotly(). Add a text aesthetic so the hover tooltip shows a useful label (an ID, a place name, a category), and restrict the tooltip with tooltip =.
Interactivity is not free — it only helps when the reader has something to explore. Good fits: a dense scatter where hover reveals individual cases, many overlapping groups the reader can toggle, or a map they can pan and zoom. If your figure makes a single point, a clean static plot from last week is often the better choice.
Exercise 2.2: Do you have spatial data?
If any of your data has a location — coordinates, place names, regions — map it.
- Coordinates or place names →
leaflet. No coordinates? Geocode place names withtidygeocoder::geocode(address, method = "osm")(free, no API key) to getlat/long. - Region boundaries →
geom_sf()(static) oraddPolygons()(interactive), joining your data to ansfobject by region name.
No spatial data? Skip this and instead add a second interaction to your Ex 2.1 plot — box-/lasso-select via highlight_key(), or a plot_ly() box plot of a distribution.
Reflection log
~10 min
Take a few minutes to add this week’s entry to your reflection log. The new interactive plot and the reflection log entry can go in one commit.
End-of-practical checklist
Before you leave today, make sure your group has:
Resources
- htmlwidgets for R — the ecosystem behind
plotly,leaflet,DT - Interactive web-based visualisation with plotly (Sievert) — the plotly reference
- Leaflet for R — building maps layer by layer
- Simple Features (
sf) andgeom_sf()— geospatial data in R - Quarto interactivity (OJS) — reactive views without a Shiny server