This article demonstrates how to construct a bubble map using ggplot2. It guides you through the process of plotting GPS coordinates on a map, with the bubbles’ size and color representing different data variables.
This post explains how to make a bubble map with ggplot2.
A bubble map is like a bubble chart, but with a map in the background. As input you need:
This post provides a step-by-step approach to build the map beside, showing the 1000 biggest cities of the UK.
The first step is to get the boundaries of the zone that interests you. Several option are available in R, as extensively described in the background map section of the gallery.
Briefly, you can find this information somewhere under the shapefile format or under the geoJSON format. You can also load google like backgrounds with the ggmap library.
This post uses the last approach: it loads the giscoR
package that provides the boundaries of every world country.
# Libraries
library(ggplot2)
library(dplyr)
# Get the world polygon and extract UK
library(giscoR)
UK <- gisco_get_countries(country = "UK", resolution = 1)
The second step is to load a data frame with the info of the bubble
you want to draw. The maps
library provides a list of the
biggest cities in the world. Let’s use it to get information on the
UK.
ggplot2
makes it a breeze to map both information on the
same graph. Use geom_sf()
for the shape of UK first, and
add your scatter plot on it with geom_point()
.
On the right figure, note the use of the ggrepel
library
to avoid overlapping between city names.
# Left chart
ggplot() +
geom_sf(data = UK, fill = "grey", alpha = 0.3) +
geom_point(data = data, aes(x = long, y = lat)) +
theme_void() +
ylim(50, 59)
# Second graphic with names of the 10 biggest cities
library(ggrepel)
ggplot() +
geom_sf(data = UK, fill = "grey", alpha = 0.3) +
geom_point(data = data, aes(x = long, y = lat, alpha = pop)) +
geom_text_repel(
data = data %>% arrange(pop) %>% tail(10),
aes(x = long, y = lat, label = name), size = 5
) +
geom_point(
data = data %>% arrange(pop) %>% tail(10), aes(x = long, y = lat),
color = "red", size = 3
) +
theme_void() +
ylim(50, 59) +
theme(legend.position = "none")
Now we want to add another information. The number of inhabitant per city will be mapped to the colour and the size of the bubbles.
Note that the order of city matters! It is advised to show the most important information on top (center). This can been done sorting your dataset before making the plot.
# Left: use size and color
ggplot() +
geom_sf(data = UK, fill = "grey", alpha = 0.3) +
geom_point(data = data, aes(x = long, y = lat, size = pop, color = pop)) +
scale_size_continuous(range = c(1, 12)) +
scale_color_viridis_c(trans = "log") +
theme_void() +
ylim(50, 59)
# Center: reorder your dataset first! Big cities appear later = on top
data %>%
arrange(pop) %>%
mutate(name = factor(name, unique(name))) %>%
ggplot() +
geom_sf(data = UK, fill = "grey", alpha = 0.3) +
geom_point(aes(x = long, y = lat, size = pop, color = pop), alpha = 0.9) +
scale_size_continuous(range = c(1, 12)) +
scale_color_viridis_c(trans = "log") +
theme_void() +
ylim(50, 59) +
theme(legend.position = "none")
# Right: just use arrange(desc(pop)) instead
data %>%
arrange(desc(pop)) %>%
mutate(name = factor(name, unique(name))) %>%
ggplot() +
geom_sf(data = UK, fill = "grey", alpha = 0.3) +
geom_point(aes(x = long, y = lat, size = pop, color = pop), alpha = 0.9) +
scale_size_continuous(range = c(1, 12)) +
scale_color_viridis_c(trans = "log") +
theme_void() +
ylim(50, 59) +
theme(legend.position = "none")
We can custom a little bit this figure for a better looking result (first image of this post).
Note that here the legend shows both the size, the color and the transparency on the same circles. This is possible only if these 3 informations are redundant, with the same name, transformation and breaks.
Note that here the legend shows both the size, the color and the transparency on the same circles. This is possible only if these 3 informations are redundant, with the same name, transformation and breaks.
# Create breaks for the color scale
mybreaks <- c(0.02, 0.04, 0.08, 1, 7)
# Reorder data to show biggest cities on top
data <- data %>%
arrange(pop) %>%
mutate(name = factor(name, unique(name))) %>%
mutate(pop = pop / 1000000)
# Build the map
data %>%
ggplot() +
geom_sf(data = UK, fill = "grey", alpha = 0.3) +
geom_point(aes(x = long, y = lat, size = pop, color = pop, alpha = pop),
shape = 20, stroke = FALSE
) +
scale_size_continuous(
name = "Population (in M)", trans = "log",
range = c(1, 12), breaks = mybreaks
) +
scale_alpha_continuous(
name = "Population (in M)", trans = "log",
range = c(0.1, .9), breaks = mybreaks
) +
scale_color_viridis_c(
option = "magma", trans = "log",
breaks = mybreaks, name = "Population (in M)"
) +
theme_void() +
guides(colour = guide_legend()) +
ggtitle("The 1000 biggest cities in the UK") +
theme(
legend.position = c(1, 0.6),
text = element_text(color = "#22211d"),
plot.margin = margin(r = 2, l = 2, unit = "cm"),
plot.background = element_rect(fill = "#f5f5f2", color = NA),
panel.background = element_rect(fill = "#f5f5f2", color = NA),
plot.title = element_text(size = 14, hjust = 0.5, color = "#4e4d47"),
legend.title = element_text(size = 8),
legend.text = element_text(size = 8)
)
Last but not least, plotly
allows to quickly get an
interactive version. This is really handy since it
allows to zoom on the map and hover a city to know its name and
population!
This mainly relies on the ggplotly()
function that
converts a ggplot2 object into a plotly, interactive
object.
# Load the plotly package
library(plotly)
# Rorder data + Add a new column with tooltip text
data <- data %>%
arrange(pop) %>%
mutate(name = factor(name, unique(name))) %>%
mutate(mytext = paste(
"City: ", name, "\n",
"Population: ", pop,
sep = ""
))
# Make the map (static)
p <- data %>%
ggplot() +
geom_sf(data = UK, fill = "grey", alpha = 0.3) +
geom_point(aes(
x = long, y = lat, size = pop, color = pop, text = mytext,
alpha = pop
)) +
scale_size_continuous(range = c(1, 9)) +
scale_color_viridis_c(option = "inferno", trans = "log") +
scale_alpha_continuous(trans = "log") +
theme_void() +
theme(legend.position = "none")
p <- ggplotly(p, tooltip = "text")
p
# save the widget in a html file if needed.
# library(htmlwidgets)
# saveWidget(p, file=paste0( getwd(), "/HtmlWidget/bubblemapUK.html"))
This post explains how to make a bubble map with ggplot2.
You might be interested in this post that combines bubble map with a cartogram, and more generally in the bubble map section of the gallery.
👋 After crafting hundreds of R charts over 12 years, I've distilled my top 10 tips and tricks. Receive them via email! One insight per day for the next 10 days! 🔥