Skip to Content
Course content

113: Choropleth Maps in R

Click on the "Edit" button in the top corner of the screen to edit your slide content.

When you first decide to build a choropleth map—those maps where regions are shaded based on a value—your instinct will probably be to treat it like any other ggplot2 visualization. You'll think, "I have a shapefile for the borders and a CSV for the data; I'll just join them and plot the polygons." This is exactly where most people get stuck in a loop of coordinate system errors and mismatched join keys.

The Trap of Treating Maps Like Polygons

The naive approach usually involves loading a shapefile using an older package like sp or trying to use geom_polygon(). I've seen a lot of developers do this because the documentation for older R projects still suggests it. The problem is that geom_polygon() expects you to manage the x and y coordinates manually. You end up with a data frame that is massive and redundant, and if you want to change the map's projection—say, moving from a flat Mercator projection to something that doesn't make Greenland look larger than Africa—you're in for a world of pain.

Furthermore, the "naive" way often leads to the NA nightmare. You join your EV adoption data to your state map, and suddenly half the map is grey. Why? Because your CSV says "California" but your shapefile says "Calif." or uses a FIPS code. If you're treating the map as just a collection of lines and points, debugging these joins is tedious because you can't easily "see" which geometry is failing until the whole plot renders (or crashes).

Why Simple Features (sf) Changed the Game

The better way is to use the sf package. If you aren't using sf, you're fighting the language. The brilliance of sf is that it treats spatial data as a standard data.frame (or tibble) with one special twist: a geometry column. This means you can use all your favorite dplyr verbs—filter(), mutate(), left_join()—without worrying about breaking the spatial integrity of the object.

library(sf)
library(ggplot2)

# The modern way: read the shapefile into an sf object
states_map <- st_read("us_states.shp")

# Join the data just like a normal table
map_data <- states_map %>%
  left_join(ev_stats, by = "state_name")

# Plot using the dedicated geom_sf
ggplot(data = map_data) +
  geom_sf(aes(fill = adoption_rate)) +
  scale_fill_viridis_c()

By using geom_sf(), ggplot2 automatically understands the Coordinate Reference System (CRS). You don't have to manually specify the axes or the projection; the function looks at the geometry column and figures it out. It's cleaner, faster, and significantly harder to break.

The Trade-off: Precision vs. Performance

Now, there is a catch. sf objects can become incredibly heavy. If you're mapping every single census tract in the US, your R session might crawl to a halt. This is the trade-off: sf gives you an intuitive, data-frame-like experience, but it stores high-precision coordinates for every vertex of every polygon.

When I'm working with high-resolution maps, I always use st_simplify(). It reduces the number of vertices in your polygons. For a choropleth map, you usually don't need millimeter-precision on a coastline; simplifying the geometry by even a small fraction can reduce your file size by 90% without any visible loss in quality on a standard screen. I'd rather have a map that renders in 200ms with slightly "blockier" coastlines than one that takes 20 seconds to load every time I tweak a color.

Finally, a quick word on aesthetics: avoid the default rainbow palettes. For choropleths, always use a sequential palette (like viridis). Since you're representing a gradient of value, your readers' brains need a linear progression of color to interpret the data correctly. Anything else is just adding noise to your signal.




📋 Practical Task

Exercise: Mapping Renewable Energy Adoption by State

You have been provided with a shapefile of US states (us_states.shp) and a dataset (energy_data.csv) containing the percentage of electricity generated from renewable sources for each state. However, the energy dataset uses state abbreviations (e.g., "NY", "TX"), while the shapefile uses full state names (e.g., "New York", "Texas").

Write a script to:

  • Load the shapefile using the sf package.
  • Create a mapping table or use a helper package to align the state abbreviations in the CSV with the full names in the shapefile.
  • Join the energy data to the spatial object using a left_join.
  • Create a choropleth map using geom_sf().
  • Apply the viridis color scale to represent the renewable energy percentage.
  • Simplify the map geometry using st_simplify() to ensure the final plot renders efficiently.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.