# Core data & visualization
library(gapminder)
library(tidyverse)
library(ggplot2)
library(ggthemes)
library(scales)
library(htmltools)
# Interactivity
library(plotly)
library(reactable)
library(gganimate)
library(leaflet)
# Geo + data join
library(countrycode)
library(maps)
We begin our exploration of the Gapminder dataset with an interactive table using the reactable package. This table allows users to filter and sort the data on-the-fly, directly from the rendered HTML page. By enabling the filterable = TRUE option, filters appear above each column, making it easy to narrow down by year, continent, or specific country. This is a useful starting point for understanding the structure of the data before diving into plots.
table <- reactable(gapminder, filterable = TRUE)
table
Next, we use ggplot2 to create a simple scatterplot. This plot shows the relationship between a country’s GDP per capita and its life expectancy. These two metrics are often linked: wealthier countries tend to have better healthcare, nutrition, and living conditions, leading to longer life spans.
ggplot(gapminder, aes(gdpPercap, lifeExp)) +
geom_point()
Here, ggplot() initializes the plot and maps GDP per capita to the x-axis and life expectancy to the y-axis using the aes() function. The geom_point() layer adds individual data points. Although basic, this plot is powerful: each point represents a country in a specific year, giving us a quick view of global health and wealth trends.
To make the scatterplot more informative, we enhance it by introducing color coding by continent. This way, we can see how different regions of the world compare in terms of development. A custom color palette is defined, and the scale_color_manual() function applies it to the plot.
myColors <- c("Africa" = "#FFC1B4", "Americas" = "#F38C79", "Asia" = "#6e6c6b",
"Europe" = "#80CDC1", "Oceania" = "#034C53")
ggplot(gapminder, aes(x = gdpPercap, y = lifeExp, color = continent)) +
geom_point(alpha = 0.7, size = 3) +
scale_color_manual(values = myColors) +
labs(
title = "Wealth vs Health Across the Globe (1952–2007)",
subtitle = "Each point is a country–year observation",
x = "GDP per capita (2007 USD)",
y = "Life expectancy (years)",
color = "Continent"
) +
theme_gdocs()
Using theme_gdocs() provides a clean, modern aesthetic suitable for presentations and publications. With these changes, the plot is no longer just a cloud of dots—it communicates geographic patterns clearly. Improved readability with meaningful colors and a clean theme.
The next step is to transform our styled scatterplot into an interactive one. The ggplotly() function from the plotly package takes a static ggplot object and converts it into an interactive widget. Users can now zoom into dense areas, hover to see data values, and explore relationships in more depth.
p <- ggplot(gapminder, aes(x = gdpPercap, y = lifeExp, color = continent)) +
geom_point(alpha = 0.7, size = 3) +
scale_color_manual(values = myColors) +
labs(
title = "Wealth vs Health Across the Globe (1952–2007)",
subtitle = "Interactive: hover to explore",
x = "GDP per capita (2007 USD)",
y = "Life expectancy (years)",
color = "Continent"
) +
theme_gdocs()
ggplotly(p)
This approach requires no rewriting of the original plot—ggplotly() wraps the existing plot and adds interactivity. It’s an ideal choice when working in R Markdown or Shiny apps.
While interactive plots are helpful, adding rich hover text makes them significantly more insightful. In this version, we embed detailed information such as the country name, year, GDP per capita, and life expectancy directly into the tooltips.
p2 <- gapminder %>%
ggplot(aes(x = gdpPercap, y = lifeExp, color = continent,
text = paste0("Country: ", country,
"<br>Year: ", year,
"<br>GDP per capita: $", round(gdpPercap),
"<br>Life expectancy: ", round(lifeExp, 1), " years"))) +
geom_point(alpha = 0.7, size = 3) +
scale_color_manual(values = myColors) +
labs(
title = "Wealth vs Health: Hover Tooltips",
x = "GDP per capita (2007 USD)",
y = "Life Expectancy",
colour = "Continent"
) +
theme_gdocs()
ggplotly(p2, tooltip = "text") %>%
layout(dragmode = "zoom") %>%
config(displayModeBar = FALSE)
By using the text aesthetic inside aes(), and then setting tooltip = “text” in ggplotly(), we ensure that only our custom content shows when the user hovers over points. This makes the plot both interactive and informative, without cluttering the visual layout.
To add a temporal dimension, we build an animated scatter plot using plot_ly(). Each frame of the animation represents a different year, allowing viewers to watch how countries move through the GDP-life expectancy space over time.
plot_ly(
data = gapminder,
x = ~gdpPercap, y = ~lifeExp, frame = ~year,
color = ~continent, colors = myColors,
text = ~paste("Country:", country,
"<br>Year:", year,
"<br>GDP:", round(gdpPercap),
"<br>LifeExp:", round(lifeExp, 1)),
hoverinfo = "text", type = 'scatter', mode = 'markers',
marker = list(size = 10, opacity = 0.7)
) %>%
layout(title = "Wealth vs Health Over Time (1952–2007)",
xaxis = list(title = "GDP per capita (log scale)", type = "log"),
yaxis = list(title = "Life Expectancy"),
legend = list(title = list(text = "<b>Continent</b>"))) %>%
animation_opts(frame = 1000, transition = 0, redraw = FALSE)
Here, the frame = ~year argument tells plot_ly() to animate the scatter plot over time. The x-axis represents GDP per capita (on a log scale), and the y-axis represents life expectancy. Hovering over a point reveals additional information, including the country name, year, GDP, and life expectancy. The animation allows us to observe historical trends, such as improvements in health and economic conditions across regions.
To focus more closely on regional patterns, we break the animation into separate panels for each continent. This approach reduces visual noise and helps reveal differences that might be hidden in the global view.
plots <- gapminder %>%
split(.$continent) %>%
lapply(function(df) {
plot_ly(
data = df,
x = ~gdpPercap,
y = ~lifeExp,
frame = ~year,
type = 'scatter',
mode = 'markers',
color = ~continent,
colors = myColors,
text = ~paste0("Country: ", country,
"<br>Year: ", year,
"<br>GDP per capita: $", round(gdpPercap, 0),
"<br>Life expectancy: ", round(lifeExp, 1), " years"),
hoverinfo = "text"
)
})
subplot(
plots,
nrows = 2,
shareX = TRUE,
shareY = TRUE,
titleX = FALSE,
titleY = FALSE,
margin = 0.03,
which_layout = "merge"
) %>%
layout(
title = "Wealth vs Health Across the Globe (1952–2007)",
xaxis = list(title = "GDP per capita (2007 USD, log scale)", type = "log", showticklabels = TRUE ),
yaxis = list(title = "Life expectancy at birth (years)"),
legend = list(title = list(text = "<b>Continent</b>")),
margin = list(l = 70, r = 30, b = 80, t = 80)
) %>%
animation_opts(frame = 1000, transition = 0, redraw = FALSE) %>%
config(displayModeBar = TRUE)
Here, the data is split using split() and lapply() creates a plot_ly() object for each subset. subplot() then arranges these side-by-side. The resulting visualization enables direct comparison between continents, each progressing through time in parallel.
Heatmaps are ideal for identifying patterns across two dimensions. In this case, we calculate average life expectancy by continent and year, then use a tile plot where color represents the value.
heatmap_data <- gapminder %>%
group_by(continent, year) %>%
summarise(lifeExp = mean(lifeExp), .groups = "drop")
p3 <- ggplot(heatmap_data, aes(x = factor(year), y = continent, fill = lifeExp,
text = paste("Year:", year, "<br>LifeExp:", round(lifeExp,1)))) +
geom_tile() +
scale_fill_viridis_c() +
labs(title = "Avg Life Expectancy by Continent-Year", x = "Year", y = "Continent", fill = "Life Expectancy") +
theme_gdocs()
ggplotly(p3, tooltip = "text")
By converting this plot with ggplotly(), users can hover over each tile to see precise values. This makes the chart useful both for visual comparison and precise lookup.
We now shift focus to GDP growth over time. This line chart shows how average GDP per capita has changed for each continent from 1952 to 2007. Line charts are ideal for temporal comparisons.
gdp_data <- gapminder %>%
group_by(continent, year) %>%
summarise(
gdpPercap = mean(gdpPercap),
lifeExp = mean(lifeExp),
pop = sum(pop),
.groups = "drop"
)
# Create the line plot
p <- ggplot(gdp_data, aes(
x = year,
y = gdpPercap,
color = continent,
group = continent,
text = paste0(
"Continent: ", continent,
"<br>Year: ", year,
"<br>Avg GDP per Capita: $", comma(gdpPercap),
"<br>Avg Life Expectancy: ", round(lifeExp, 1),
"<br>Total Population: ", comma(pop)
)
)) +
geom_line(size = 1.2) +
labs(
title = "GDP per Capita Over Time by Continent",
x = "Year",
y = "Average GDP per Capita",
color = "Continent"
) +
scale_color_manual(values = myColors)+
theme_gdocs()
# Make it interactive
ggplotly(p, tooltip = "text")
Each line represents a continent, and geom_line() plots the time series. We again convert the plot using ggplotly() to enable interaction. Viewers can isolate regions, track peaks, and compare economic trajectories.
Finally, we use the leaflet package to display 2007 data on a map. Each country is represented by a circle, where the size reflects population and the label shows life expectancy. Geographic visualization provides valuable context that’s hard to capture in charts alone.
gap2007 <- gapminder %>% filter(year == 2007)
gap2007$iso3 <- countrycode(gap2007$country, "country.name", "iso3c")
data("world.cities", package = "maps")
city_coords <- world.cities %>%
filter(capital == 1) %>%
select(country = country.etc, lat, lng = long)
map_data <- gap2007 %>%
left_join(city_coords, by = "country") %>%
filter(!is.na(lat) & !is.na(lng))
leaflet(map_data) %>%
addTiles() %>%
addCircleMarkers(
lng = ~lng, lat = ~lat,
radius = ~sqrt(pop) / 1000,
color = "blue", stroke = FALSE, fillOpacity = 0.5,
label = ~lapply(paste0(
"<b>", country, "</b><br/>",
"Population: ", format(pop, big.mark = ","), "<br/>",
"Life Expectancy: ", round(lifeExp, 1)
), HTML),
labelOptions = labelOptions(textOnly = FALSE)
) %>%
addLegend(
position = "bottomright",
colors = "blue",
labels = "Circle size ~ population",
title = "Legend"
)
The code joins geographic coordinates to the dataset using country names, then creates the map with leaflet(). Circles are scaled using population and customized with tooltips using label. This final visualization ties together the themes of health, wealth, and geography.
As demonstrated throughout this document, R provides a remarkably versatile environment for creating visual narratives. Whether you’re using static charts for clarity, interactive plots for exploration, or animated sequences to show change over time, the tools are here to help you bring data to life.
Visualisation in R is not just about aesthetics—it’s about clarity, impact, and communication. The more you explore libraries like ggplot2, plotly, and leaflet, the more fluent you’ll become in expressing insights in ways that resonate. There’s no limit to what you can create once you understand the grammar of graphics and the rich ecosystem around it.
This document just scratches the surface. Keep experimenting, keep asking questions, and don’t hesitate to dive deeper into the resources below.
Plotly for R – Official
Documentation
Build interactive plots like scatterplots, bar charts, and heatmaps with
ease. Integrates well with ggplot2 or can be used
standalone.
Interactive Data
Visualization with R – plotly & shiny (Carson
Sievert)
A comprehensive, free online book that teaches how to create web-based
interactive visualizations using plotly,
ggplotly(), and shiny.
gganimate – Animate
ggplots over time
Animate changes in your data using ggplot2. Ideal for
storytelling with time-series or categorical transitions.
reactable –
Interactive Tables in R
Create interactive data tables with rich features like search, filters,
conditional formatting, and sparkline support.
The R
Graph Gallery – Interactive Visualization
A vast collection of examples using packages like plotly,
leaflet, dygraphs, and
highcharter. Great for inspiration.
Data
Visualization – R for Data Science (Hadley
Wickham)
A conceptual and practical introduction to ggplot2,
covering the grammar of graphics and tidy data principles.
Interactive
Maps in R – Bhaskar V. Karambelkar (useR! 2017)
A practical guide to building rich, interactive maps in R using the
leaflet package and spatial data tools.