Choropleth Map
A thematic map that shades geographic regions in proportion to a data variable — the darker the shade, the higher the value. The most widely used way to visualize how a rate, ratio, or density varies across a familiar geography, from elections and census reports to public-health dashboards.
// 01 — The chart
What it looks like
A choropleth map showing population density. Darker regions indicate higher density, letting you spot geographic hot spots at a glance.
// 02 — Definition
What is a choropleth map?
A choropleth map (from the Greek choros, “area,” and plethos, “multitude”) is a thematic map in which predefined geographic regions — countries, states, counties, postal codes, or any other administrative unit — are shaded according to a statistical variable. The intensity, lightness, or hue of each region’s color is proportional to the value it represents, so the eye reads the spatial pattern of that variable in a single glance. Choropleths are the most widely used class of thematic maps and are the default visual when journalists, statisticians, and public-health officials need to communicate how a rate varies across a familiar geography.
The encoding works because human visual perception is excellent at picking up regions of similar lightness and stitching them into clusters — we instinctively read darker patches as “more” and lighter patches as “less” when the palette is sequential, and as “positive” versus “negative” when it diverges from a meaningful midpoint. The trade-off is that color is poor at communicating precise values: a reader can rank regions and spot clusters reliably, but rarely tell whether a particular county sits at 78% versus 82%. Choropleths therefore do their best work when the headline is the spatial pattern (where) and the legend or accompanying table carries the precision (how much).
Two non-negotiable constraints sit at the heart of every well-made choropleth. First, the data must be tied to enumeration units — discrete, non-overlapping geographic boundaries the audience can recognize without help. Second, the variable must be a rate, ratio, percentage, density, or index — not a raw count — because color fills the entire region and an unnormalized count therefore encodes region size as much as it encodes the variable. Choropleths that violate either constraint — mapping unfamiliar boundaries or mapping totals — routinely turn into population maps in disguise, telling the reader something different from what the analyst intended.
Origin: The first true choropleth map was lithographed by French engineer Charles Dupin in 1826 to show levels of education across the départements of France. The technique was named more than a century later: American geographer John Kirtland Wright coined “choropleth” in his 1938 paper Problems in Population Mapping, where he also articulated the still-canonical rule that choropleths should map rates rather than counts.
// 03 — When to use it
When a choropleth is the right choice
- Your data is tied to well-known geographic boundaries (countries, states, provinces, counties).
- The variable is a rate, ratio, percentage, density, or index — not a raw count.
- You want to show broad spatial patterns, gradients, or clusters at a glance.
- Your audience already recognizes the geography being shown.
- You have a value for most or all of the regions on the map.
- The headline question is comparing regions on a single variable across a territory.
// 04 — When NOT to use it
When a choropleth is the wrong choice
- Your data is raw counts — large regions visually dominate even when their rate is low.
- Region sizes vary so dramatically that small but important areas vanish (Manhattan vs Wyoming).
- You need to read precise numeric values from the chart — color encoding is imprecise.
- Your data is point-based (events, addresses) rather than area-based — use a dot density or bubble map.
- You have very few regions — a sorted bar chart is usually clearer than a sparsely shaded map.
- You’re asking a question about flows, networks, or relationships between regions, not values within them.
- The audience is unfamiliar with the geography — the spatial pattern won’t register as information.
- The chart will be reproduced in grayscale or low-quality print where shade differences collapse.
// 05 — Data requirements
What your data needs to look like
Before building the chart, your dataset needs to fit a specific shape. Use this checklist to confirm yours does.
Shape
One row per region: a stable region key, the value (a rate or ratio), and a polygon geometry loaded from a separate boundary file.
Minimum rows
5 regions
Maximum rows
~3,000 regions (counties / municipalities)
A stable identifier for each geographic region used to join the data to the boundary file. Common keys are FIPS codes for US counties/states, ISO 3166 codes for countries, NUTS codes for European regions, or the official region name when nothing else is available.
The variable being mapped. For a choropleth this should be a rate, ratio, percentage, density, or index — not a raw count — so that region size doesn’t bias the color encoding.
The polygon outlining each region, usually loaded from a Shapefile, GeoJSON, or TopoJSON file. Match the projection to the region (Albers for the contiguous US, Mercator only for global web maps, Robinson for world thematic maps).
Optional grouping used for small multiples (e.g., one map per year, party, demographic). Lets you turn a single choropleth into a faceted comparison without re-projecting the geometry each time.
| region_id | region_name | rate |
|---|---|---|
| 06 | California | 85.2 |
| 48 | Texas | 78.4 |
| 36 | New York | 88.1 |
| 12 | Florida | 81.7 |
| 17 | Illinois | 83.9 |
Always store the region key as a string — leading zeros (e.g., FIPS “06”) get stripped when you treat the column as a number, breaking the join with the boundary file.
// 06 — Anatomy
Parts of a choropleth map
A choropleth has only a handful of moving parts, but every one of them is load-bearing. Skipping the legend, mishandling missing data, or picking the wrong palette undermines the encoding even when the geometry is perfect.
// 07 — Step-by-step
Step-by-step: how to build a good choropleth
A ten-step recipe that works regardless of the tool. Walk through it the first few times and the moves become automatic; skip a step — especially the rate, the projection, or the no-data swatch — and the chart usually shows it.
- 1
Pick the question the map will answer
A choropleth answers “how does this rate vary across regions?” Write the question down before you build anything. If your question is about magnitude (totals), point events, or flows between regions, switch to a cartogram, dot map, or flow map now — not after you have wrangled the geometry. - 2
Get matching boundary geometry
Download the lowest-resolution boundary file that still looks credible at your output size. Use Natural Earth for world maps, the US Census TIGER/Line files for US administrative units, GADM for country sub-divisions, and Eurostat’s NUTS files for Europe. Verify the projection before you load. - 3
Join your data on a stable key
Merge the data table to the boundary file on a code that won’t drift over time — FIPS, ISO 3166, NUTS, or the official region identifier. Spell-checking on names is fragile (“Ivory Coast” vs “Côte d’Ivoire”). Audit the unmatched rows after the join. - 4
Normalize to a rate
Divide the count by the appropriate denominator: population (per capita), area (per square kilometer), households, students. Without this step the map shows region size more than it shows the variable. The exception is when the variable is already a rate or an index. - 5
Choose a palette that matches the data
Pick a sequential palette (Viridis, YlOrRd, Blues) for one-directional variables and a diverging palette (RdBu, BrBG) for variables with a meaningful midpoint. Use ColorBrewer or the viridis family because they’ve been tested for color-vision deficiency and printing. - 6
Choose a classification scheme
Decide whether to bin into 4–7 classes (Quantiles, Natural Breaks/Jenks, Equal Interval) or to use an unclassed continuous gradient. Jenks is the default for skewed real-world data. Always state the classification and number of bins in a caption so readers know what they’re seeing. - 7
Pick a sensible projection
Use an equal-area projection so equal data values are encoded by equal-area patches: Albers Equal Area for the contiguous US, Lambert Azimuthal Equal Area for continents, Robinson or Equal Earth for world maps. Web Mercator inflates regions toward the poles and is rarely the right choice for thematic data. - 8
Style boundaries and labels
Use thin, neutral boundary lines so they don’t compete with the color fill, drop a no-data hatching for missing regions, and label only a small number of geographically important features (capital cities, key states). Anything more becomes visual noise. - 9
Add a legend that includes units
The legend must show the variable name, the unit (e.g., “deaths per 100,000”), the class breaks, and the no-data swatch. A horizontal colorbar reads better in dashboards than a stacked legend. Place it where the eye lands first — usually below the title. - 10
Annotate the takeaway, then ship
Replace the default title with the headline (e.g., “Broadband adoption is highest in the Northeast corridor”), call out one or two key regions with thin connector lines, and write a one-sentence caption describing the spatial pattern in plain English for screen readers and skim-readers.
// 08 — Real-world examples
Where you’ll see choropleth maps used
Choropleths show up most in three places: government statistics, public-health dashboards, and election coverage. Each context has its own conventions, but they all reward the same fundamentals: a familiar geography, a normalized rate, and a sequential or diverging palette.
Government: Census population density
The U.S. Census Bureau publishes annual choropleth maps of population density by county and tract, shading each unit by people per square kilometer. Reviewers can read the rural–urban gradient, spot the megalopolis bands, and identify counties whose density has shifted between censuses. Population is normalized to land area so large-but-empty counties don’t dominate the visual.
Public statisticsPublic health: COVID-19 case rates
Throughout the pandemic, agencies including the CDC, ECDC, and Johns Hopkins published choropleth dashboards of cases per 100,000 people by state and country. The maps were updated daily and used a single-hue sequential palette so darker meant more cases. Rate normalization made comparisons across regions of wildly different population meaningful.
Health analyticsJournalism: Election results by state
Election-night graphics by The New York Times, the Financial Times, and the BBC use a diverging red–blue (or red–yellow) choropleth to show vote share or victory margin by state, district, or constituency. The diverging palette is the right choice because the variable is bipolar around 50%. Most outlets pair the map with a population-weighted cartogram so reading the popular vote isn’t left to area alone.
Data journalismEducation: Broadband adoption by county
The U.S. Federal Communications Commission and the National Telecommunications and Information Administration publish county-level choropleth maps of household broadband adoption. The variable is a percentage, the palette sequential. The maps highlight digital-divide patterns that policymakers use to target subsidy programs and that researchers use to study correlations with income, education, and race.
Policy analytics// 09 — Variations
Types of choropleth maps
The basic choropleth has several important variants, each suited to slightly different data situations. The headline rule is the same as ever: pick the variant whose strengths match your question.
Classed choropleth
Groups the value into 4–7 discrete classes (Quantiles, Jenks Natural Breaks, or Equal Interval). The default for most published maps because it’s easier to read and maps cleanly to a stepped legend.
Unclassed choropleth
Uses a continuous color gradient with no discrete steps. Preserves nuance but can be harder to compare individual regions; pairs well with an interactive tooltip that exposes the exact value.
Bivariate choropleth
Encodes two variables simultaneously through a 3×3 or 4×4 color matrix. Reveals correlations between, say, income and life expectancy at a glance, but takes practice to read.
Diverging choropleth
Two hues diverging from a neutral midpoint. Use it when the variable has a meaningful zero, midpoint, or threshold (election margin, anomaly vs national average).
// 10 — Comparisons
Choropleth vs other geospatial chart types
The Geospatial family is full of charts that look superficially similar to a choropleth. The differences matter — picking the wrong one changes what your reader is allowed to conclude.
Choropleth vs cartogram
Both shade regions by a variable, but a choropleth keeps real-world geography while a cartogram distorts region sizes so area encodes the value. Choose the choropleth when geographic literacy matters, the cartogram when magnitude does.
Choropleth map
Regions stay at their true size and shape; color carries the value. Easy to recognize and easy to build, but big regions visually dominate even at modest values.
- Familiar geography, easy to read
- Suffers from large-region visual bias
- Best for rates and densities, not counts
Cartogram
Region areas are scaled to match the data value, so a tiny but populous region (Singapore, the District of Columbia) becomes prominent. Trades geographic accuracy for honest magnitude.
- Region area encodes magnitude faithfully
- Distorted shapes can confuse novices
- Hard to build by hand — needs a solver
Choropleth vs dot density map
A choropleth assumes the variable is uniform inside each region; a dot density map sprays one dot per N units across the region’s area. Dot density preserves within-region variation; choropleth flattens it.
Choropleth map
Single color fill per region. Communicates a clean overall pattern but hides any variation inside the region. Works best for rates aggregated to administrative units.
- One color per region
- Cleaner, easier to compare across regions
- Hides intra-region variation
Dot density map
Each dot stands for a fixed number of units (1 dot = 100 households). Naturally encodes raw counts and shows where inside the region the value concentrates.
- Handles raw counts honestly
- Reveals within-region clusters
- Dot placement can imply false precision
Choropleth vs proportional symbol (bubble) map
Both show a single variable across regions. A choropleth shades the entire region, a bubble map plants a sized circle at each region’s centroid. Bubble maps handle counts cleanly; choropleths handle rates cleanly.
Choropleth map
Color encodes a rate or ratio. Best when every region has a value and you want to show a continuous spatial gradient.
- Color = rate, ratio, or density
- Handles continuous spatial patterns
- Distorted by raw counts
Proportional symbol (bubble) map
Circle area encodes the value at the region centroid. Handles raw counts honestly, leaves the geographic boundaries unshaded, but circles can overlap in dense areas.
- Bubble area = raw count or magnitude
- Centroids overlap in dense regions
- Less reliant on equal-area projection
Choropleth vs hexbin map
Choropleths inherit the political boundary, however jagged or unequal. Hexbin maps replace those boundaries with a regular grid of equally sized hexagons, removing the large-region bias at the cost of geographic recognizability.
Choropleth map
Boundaries match the real administrative units. Recognizable, accurate, but encodes the unequal area of those units into the visual.
- Real boundaries, familiar geography
- Unequal areas bias the encoding
- Best for ratios and rates
Hexbin map
Aggregates the variable into uniform hexagonal cells. Equal-area cells make magnitude comparisons honest and reveal clusters that boundaries can hide.
- Uniform cell size removes area bias
- Loses the original boundaries
- Best at high zoom or with dense point data
// 11 — Common mistakes
Mistakes to watch out for
Almost every misleading choropleth in the wild fails the same handful of ways. If you only memorize half a dozen rules, make them these.
Mapping raw counts instead of a rate
This is the most common and most misleading mistake on a choropleth. Because color fills the entire region, a populous state with a low per-capita rate ends up darker than a small state with a high rate. The map then reproduces the population distribution rather than the variable, even though the title and legend imply otherwise. Always normalize: per capita, per square kilometer, per household, or as a percentage of a sensible total.
Picking a rainbow palette
Rainbow palettes (red–yellow–green–blue) have no perceptual order — readers can’t tell which color means “more” without consulting the legend for every region. They also fail badly under common forms of color-vision deficiency. Use a perceptually uniform sequential palette (Viridis, ColorBrewer’s YlOrRd, Blues, Greens) for one-directional data, and a diverging palette for variables with a meaningful midpoint.
Too many color classes
Past about seven discrete classes, the eye loses the ability to assign each region’s color to the right legend swatch. The map looks more nuanced but actually communicates less. Stick to 4–7 classes for classed choropleths; if you need finer resolution use an unclassed continuous gradient with a clearly labeled colorbar legend.
Using Web Mercator for thematic data
Web Mercator inflates regions toward the poles — Greenland looks roughly the size of Africa even though Africa is fourteen times larger. For a choropleth, area is part of the visual encoding, so an equal-area projection (Albers, Lambert Azimuthal Equal Area, Equal Earth, Robinson) is essential. Web Mercator is fine for slippy navigation maps but not for thematic ones.
Treating missing data as the smallest value
If you map missing-data regions with the lightest color in the palette, readers can’t tell whether those regions had small values or no values at all. Either render no-data regions in a neutral gray, apply a hatched fill, or omit them entirely with a clear caption. Whatever you choose, the legend must distinguish “missing” from the smallest in-range value.
Ignoring the modifiable areal unit problem (MAUP)
The same data mapped at the county level versus the state level can tell different and sometimes contradictory stories, because the boundary you aggregate to is part of the encoding. Note the unit you’re using in the caption, and where space allows show one alternative scale (a state-level companion map next to the county map) to give readers a sense of the scale-sensitivity.
Over-labeling the map
Labeling every region turns a clean choropleth into a jumble of overlapping text. Choose a small number of geographic anchors (capital cities, the largest few regions, regions you’re calling out in the title) and put everything else into a tooltip or an accompanying data table. Less label, more map.
// 12 — Accessibility
Accessibility checklist
Run through this list before publishing. The chart should still communicate its message to readers using assistive technology, color-blind users, keyboard navigation, and reduced-motion settings.
- ✓
Use a ColorBrewer-safe sequential palette
WCAG 1.4.1Default to ColorBrewer’s sequential palettes (YlOrRd, YlGnBu, Blues, Greens) or the viridis family. They’ve been validated against the most common forms of color-vision deficiency and they preserve perceptual order so darker always means more. - ✓
Pick sequential vs diverging deliberately
WCAG 1.4.1Sequential palettes for one-directional data (rates, densities, counts), diverging palettes (RdBu, BrBG) for variables with a meaningful midpoint such as a national average or a 50% threshold. A wrong-palette choice misleads readers even with perfect contrast ratios. - ✓
Normalize the variable as a rate, not a count
WCAG 1.3.1Choropleth shading is read as intensity per unit area. Mapping raw counts encodes region size more than the variable, which is both misleading and fails to communicate the relationship the chart claims to show. Always divide by population, area, or another sensible denominator. - ✓
Provide a data-table fallback
WCAG 1.1.1Offer a screen-reader-accessible table that lists every region with its value, sorted by value descending. Many readers will prefer the table to the map even if they can see colors, and search-engine crawlers index it as the long description. - ✓
Write an accessible name describing the spatial pattern
WCAG 1.1.1Set an aria-label or alt text that summarizes the takeaway in plain language: “Broadband adoption ranges from 38% in rural Mississippi to 89% in coastal Massachusetts, with a clear gradient from the Southeast to the Northeast.” Avoid “Choropleth map of the United States.” - ✓
Maintain contrast on boundary lines and labels
WCAG 1.4.3Region borders should reach at least 3:1 contrast against both the lightest and the darkest fill in your palette so readers can still see the shape of every region. Place text labels on a small white halo or panel when they cross dark fills. - ✓
Distinguish missing data from low values
WCAG 1.3.1Render no-data regions with a neutral gray hatch, not the lightest fill in the palette. Otherwise an absent value is indistinguishable from the smallest reported value, which can dramatically bias readers’ mental model of the map. - ✓
Make the map keyboard-navigable when interactive
WCAG 2.1.1If the map is interactive, every region should be reachable with the Tab key in a sensible order (sorted by value or alphabetical), focus rings should be visible, and tooltips should appear on focus, not only on hover. - ✓
Respect prefers-reduced-motion on transitions
WCAG 2.3.3If you animate between time slices or zoom levels, gate the animation behind a prefers-reduced-motion: no-preference media query so motion-sensitive readers see the final state immediately.
// 13 — Best practices
Design and craft tips
The mistakes section above tells you what to avoid. The list below is the positive version: the small set of habits that separate a good choropleth from a passable one.
Always map a rate, not a raw count
Don’t use a rainbow palette
Use 4–7 classes for a classed choropleth
Don’t use Web Mercator for thematic maps
Show a no-data swatch in the legend
Don’t crowd the map with labels
Lead with a takeaway title
Don’t mix levels in the same map
// 15 — Tool instructions
How to build it in your tool of choice
Choropleths look intimidating but they’re straightforward in every modern tool. The recipes below get you to a clean, normalized, sequentially shaded choropleth in each of the most common platforms.
Microsoft Excel
Spreadsheet — ~4 min- 01Place region names (countries, US states, or recognized administrative units) in column A and the rate or ratio in column B with a header row.
- 02Highlight both columns including the headers.
- 03Open Insert → Charts → Maps → Filled Map — Excel will geocode the names against Bing Maps and shade the regions.
- 04If Excel can’t resolve a name, hover the small warning icon on the chart to see the unmatched rows; replace ambiguous names with their ISO or postal codes.
- 05Right-click the data series, choose Format Data Series, and switch the Series Color to a sequential palette with the dark end at the high values.
- 06Open Map area in the Format pane and switch to Only regions with data so blank regions don’t draw attention.
- 07Edit the chart title with a takeaway sentence and adjust the legend’s number format to include units.
Tip: Excel’s Filled Map only supports country/region/state level granularity — for county-level work you’ll need a 3D Maps tour or an external add-in.
Google Sheets
Spreadsheet — ~4 min- 01Lay out region identifiers in column A (ISO 3166 codes are most reliable for the world; full state names work in the US) and the rate in column B.
- 02Select the range and choose Insert → Chart.
- 03In the Chart editor, set Chart type to Geo chart → Regions to color whole countries or US states.
- 04Switch to the Setup tab and confirm the Region column maps to A and the Color column maps to B.
- 05Open Customize → Geo and pick the region scope (World, USA, Europe), then choose a Min color and Max color from the same hue family.
- 06Set the Mid color to the same hue’s mid value for a clean sequential ramp — not a contrasting hue that would imply a diverging scale.
- 07Edit the chart title under Chart & axis titles → Chart title and write a takeaway sentence.
Sheets’ Geo chart supports countries and US states; for finer admin units (counties, ZIPs) export the data and use Looker Studio with a Google Maps tile layer instead.
Python (GeoPandas)
Code — ~10 min- 01Install the spatial stack: pip install geopandas matplotlib mapclassify (mapclassify gives you Jenks, Quantiles, and other schemes).
- 02Read your boundary file with gdf = gpd.read_file('us_states.geojson') and inspect gdf.crs to confirm the projection.
- 03Reproject to an equal-area CRS: gdf = gdf.to_crs('ESRI:102003') for the contiguous US (Albers).
- 04Merge in your data table on the region key: gdf = gdf.merge(data, on='STATE_FIPS').
- 05Compute a rate column (e.g., gdf['rate'] = gdf['cases'] / gdf['population'] * 100_000).
- 06Plot with gdf.plot(column='rate', cmap='viridis', scheme='Quantiles', k=5, legend=True, edgecolor='white', linewidth=0.3, missing_kwds={'color':'#dcdcdc','hatch':'///'}).
- 07Strip axes with ax.set_axis_off(), set the title to a takeaway sentence, and save with plt.savefig('map.png', dpi=200, bbox_inches='tight').
If you need an interactive web version, Plotly Express’s px.choropleth() and px.choropleth_mapbox() use the same GeoJSON inputs.
R (sf + ggplot2)
Code — ~10 min- 01Install the spatial stack: install.packages(c('sf','ggplot2','dplyr','viridis')).
- 02Load the boundary file: states <- st_read('us_states.geojson') and confirm the CRS with st_crs(states).
- 03Reproject to an equal-area CRS: states <- st_transform(states, 5070) for the US Albers EPSG.
- 04Join your data: states <- left_join(states, data, by = 'state_fips').
- 05Compute the rate inside a mutate() call so the original count column stays available.
- 06Build the map with ggplot(states) + geom_sf(aes(fill = rate), color = 'white', size = 0.2) + scale_fill_viridis_c(option = 'C', name = 'Rate per 100k').
- 07Polish with theme_void(), labs(title = ...), and add geom_sf_label() only for a handful of important regions.
Use scale_fill_distiller(palette = 'YlOrRd') for a ColorBrewer sequential palette, or scale_fill_distiller(palette = 'RdBu') with a meaningful midpoint for diverging data.
JavaScript (D3.js + TopoJSON)
Code — ~15 min- 01Install dependencies: npm install d3 d3-geo topojson-client, or include them via the unpkg CDN in a script tag.
- 02Load a TopoJSON boundary file with d3.json() and a CSV of values with d3.csv() in parallel using Promise.all.
- 03Build a projection that matches your scope: d3.geoAlbersUsa() for the US, d3.geoNaturalEarth1() for the world.
- 04Create a path generator: const path = d3.geoPath(projection).
- 05Build a sequential color scale: const color = d3.scaleSequentialQuantile(values, d3.interpolateYlOrRd).
- 06Append one <path> per feature: svg.selectAll('path').data(features).join('path').attr('d', path).attr('fill', d => color(values.get(d.id))).
- 07Add a horizontal legend with d3.scaleLinear()-driven gradient stops, plus a tooltip that appears on focus and hover so the chart is keyboard-accessible.
Observable Plot has a higher-level Plot.geo() helper that handles projection, color scale, and legend in roughly five lines if you don’t need full D3 control.
Tableau
BI — ~5 min- 01Open Tableau and connect to your data source. Make sure the region column is set to a Geographic role (right-click the field → Geographic Role → Country/State/etc.).
- 02Drag the geographic field to the Detail card on the Marks shelf. Tableau auto-generates Latitude (generated) and Longitude (generated) and projects the map.
- 03Change the Marks card type from Automatic to Map (filled) so each region is shaded rather than dotted.
- 04Drag the rate measure onto the Color card. Click the Color card and switch the palette to a sequential one (Orange, Green, Blue) and set Stepped Color to 5 or 7 steps.
- 05Right-click the color legend and choose Edit Title and Format Numbers to add units (“per 100k”) and the right number of decimals.
- 06Use Map → Map Layers in the menu to suppress unnecessary base map elements (POIs, roads) so the choropleth fills carry the visual weight.
Tableau’s default projection is Web Mercator. For a thematic map use Map → Background Maps → Offline plus a manual change to an equal-area projection where supported.
Power BI
BI — ~5 min- 01In Power BI Desktop, drag the geographic field onto the canvas and pick Filled map from the Visualizations pane (this is the choropleth visual).
- 02Drag the rate measure into the Saturation (or Color saturation) well so each region is shaded by value.
- 03If region names are ambiguous, switch to the Shape map visual and load a custom TopoJSON for finer admin units (counties, NUTS regions).
- 04Open Format → Data colors and set the Diverging toggle off, choose Minimum and Maximum values, and pick a sequential ColorBrewer-style hue.
- 05Set Format → Map controls → Auto zoom on so the map crops to regions that actually have data.
- 06Edit the title with a takeaway sentence and add a clear data label or category to the legend with units.
Power BI’s Filled map is geocoded by Bing Maps; if the project requires offline rendering or non-standard borders, Shape map with a custom TopoJSON is the right choice.
// 16 — Code examples
Working code in the most common stacks
Three runnable snippets that produce visually equivalent maps — a US-state choropleth of broadband adoption with a sequential palette, white boundaries, and a no-data swatch. Replace the file paths and the data block with your own.
import geopandas as gpd
import matplotlib.pyplot as plt
# Read the boundary file (here: US states from Natural Earth or TIGER/Line).
states = gpd.read_file("us_states.geojson")
# Project to an equal-area CRS so equal data values are encoded as equal-area patches.
states = states.to_crs("ESRI:102003") # USA Contiguous Albers Equal Area
# A small example dataset: broadband adoption rate per state (%, made-up values).
data = {
"STATE_FIPS": ["06", "48", "36", "12", "17", "13", "39", "42", "37", "26"],
"rate": [85.2, 78.4, 88.1, 81.7, 83.9, 76.5, 80.3, 82.6, 75.9, 79.1],
}
df = gpd.pd.DataFrame(data)
states = states.merge(df, on="STATE_FIPS", how="left")
fig, ax = plt.subplots(figsize=(11, 7))
states.plot(
column="rate",
cmap="viridis",
scheme="Quantiles",
k=5,
legend=True,
edgecolor="white",
linewidth=0.3,
missing_kwds={"color": "#dcdcdc", "hatch": "///", "label": "No data"},
ax=ax,
)
ax.set_title("Broadband adoption is highest in the Northeast",
loc="left", fontsize=14, color="#1a1a18")
ax.set_axis_off()
plt.tight_layout()
plt.savefig("choropleth.png", dpi=200, bbox_inches="tight")
plt.show()
// 17 — FAQs
Frequently asked questions
What is a choropleth map?+
A choropleth map (from the Greek choros meaning 'area' and plethos meaning 'multitude') is a thematic map in which predefined geographic regions — countries, states, counties, or postal codes — are shaded according to a statistical variable. The color intensity of each region is proportional to the value it represents, so a darker shade typically means a larger rate, percentage, or density.
When should you use a choropleth map?+
Use a choropleth map when your data is tied to well-known administrative boundaries, when the variable can be expressed as a rate or ratio (per capita, percentage, density), and when the geographic pattern itself is the headline. They work best for medium-sized regions of comparable scale and for audiences already familiar with the underlying geography.
When should you avoid a choropleth map?+
Avoid a choropleth when your data is a raw count rather than a rate, when region sizes vary so dramatically that small areas vanish (think Manhattan vs Wyoming), when you need to read precise values from individual regions, or when the data is point-based (use a dot density or bubble map). A choropleth also struggles when many regions are missing data.
What’s the difference between a choropleth map and a cartogram?+
A choropleth keeps each region at its true geographic size and shades it by value. A cartogram distorts region sizes so that area becomes proportional to the value, fixing the visual dominance problem big regions cause on a choropleth. Cartograms reveal magnitude faithfully but sacrifice geographic accuracy, so the choice depends on whether shape or magnitude matters more.
What’s the difference between a choropleth map and a dot density map?+
Both show how a variable is distributed across space, but a choropleth assumes the value is uniform inside each region (the entire shape gets one color), while a dot density map scatters one dot per unit (or per N units) inside the boundary. Dot density preserves within-region variation and shows raw counts honestly; choropleth shows rates more cleanly but flattens internal variation.
What’s the difference between a choropleth map and a proportional symbol (bubble) map?+
A choropleth shades the entire region; a proportional symbol map keeps the region boundaries plain and places a circle (or other shape) sized to the value at the region’s centroid. Bubble maps handle raw counts naturally and avoid the large-region bias of choropleths, but they overlap when regions are tightly packed and they can’t depict spatial gradients as smoothly.
Why does a choropleth need rates instead of counts?+
Because color fills the entire region, a large region with a moderate count will visually dominate a small region with a high count. Mapping raw counts therefore tends to recapitulate the population map (or the area map) rather than the variable you actually care about. Normalizing to a rate (per 100k people, per square kilometer, per household) controls for region size and population so the color truly reflects the underlying phenomenon.
How many color classes should a choropleth use?+
Use 4 to 7 color classes for a classed choropleth. Fewer than four hides too much variation, more than seven exceeds the number of shades a typical reader can distinguish reliably. For an unclassed (continuous) choropleth, the legend should still call out roughly five reference values so readers can decode shades into numbers.
What color palette should I use for a choropleth?+
Pick a perceptually uniform sequential palette (Viridis, ColorBrewer’s YlOrRd, Blues, or Greens) when the variable is one-directional. Pick a diverging palette (RdBu, BrBG) when the variable has a meaningful midpoint such as a national average or a 50% election threshold. Avoid rainbow palettes — they have no perceptual order and cause systematic misreadings.
What category of chart is a choropleth map?+
Choropleth Map belongs to the Geospatial family of charts. Charts in that family are designed to answer questions about spatial distribution, so neighbors like cartograms, hexbin maps, dot density maps, bubble maps, and tile grid maps often work as alternatives when a choropleth doesn’t quite fit your data.
How do you read a choropleth map?+
Start with the title and legend so you know the variable, the unit, and whether the scale is sequential or diverging. Then scan the map for the darkest and lightest regions to anchor the extremes, look for spatial clusters or gradients, and finally check the legend numbers for any region you’re tempted to quote precisely — color is good for patterns and bad for exact values.
How is a choropleth map made in Python?+
The standard stack is GeoPandas, which extends pandas with a geometry column and a .plot() method backed by Matplotlib. Read your boundaries from a Shapefile, GeoJSON, or TopoJSON file, join your data table on the region code, then call gdf.plot(column='value', cmap='viridis', scheme='Quantiles', k=5, legend=True). Plotly Express also offers choropleth() for interactive web charts.
How is a choropleth map made in R?+
Use the sf package to read spatial data and ggplot2’s geom_sf() to draw it. After loading boundaries with st_read() and joining your variable on the region key, the call is ggplot(data) + geom_sf(aes(fill = value)) + scale_fill_viridis_c() + theme_void(). The tmap package is another popular option for thematic maps with first-class choropleth support.
What’s the modifiable areal unit problem (MAUP)?+
MAUP is the observation that statistical results computed over geographic regions can change dramatically when those regions are redrawn at different scales (county vs state) or shapes (zip code vs school district). A choropleth always inherits the unit it’s built on, so the same data shown at different scales can tell different stories. Note the unit you used and ideally show one alternative scale.
Are choropleth maps accessible to colorblind readers?+
They can be, but only if you choose the palette deliberately. Use ColorBrewer or Viridis-family palettes that have been tested for the common forms of color-vision deficiency. Reinforce the encoding with a numeric legend and a data-table fallback. Never rely on red-vs-green alone, and always provide a long-description text alternative summarizing the spatial pattern in words.
// 18 — References
References and further reading
Primary sources, reference texts, and the official documentation for the libraries and tools referenced throughout this guide.
- Wikipedia — Choropleth mapReferenceEncyclopedia entry covering the etymology, history, and visual encoding of choropleth maps. A solid neutral starting point with citations to Dupin and Wright.https://en.wikipedia.org/wiki/Choropleth_map
- Dupin’s 1826 lithographed map of educational levels in France is widely cited as the first true choropleth. Hosted by the Bibliothèque nationale de France via Gallica.https://gallica.bnf.fr/ark:/12148/bpt6k1054970v
- The paper in which Wright coined the term “choropleth” and laid out the canonical guidance against mapping raw counts. Available via JSTOR.https://www.jstor.org/stable/i317003
- The standard cartography textbook for decades. The chapters on classification schemes and color theory remain the canonical reference for thematic mapping.https://www.wiley.com/en-us/Elements+of+Cartography%2C+6th+Edition-p-9780471555797
- Free interactive tool for picking sequential, diverging, and qualitative palettes that are tested for color-vision deficiency, photocopying, and projector display.https://colorbrewer2.org/
- The classic primer on the unintentional and intentional ways maps mislead. The chapters on classification choice and color palette read like a checklist for honest choropleths.https://press.uchicago.edu/ucp/books/book/chicago/H/bo27548203.html
- Hands-on tutorial with real published examples covering normalization, palette choice, and accessibility. The strongest practical reference on this list.https://academy.datawrapper.de/article/333-what-to-consider-when-creating-a-choropleth-map
- WAI — Complex Images: Charts and GraphsAccessibilityWeb Accessibility Initiative guidance on making charts accessible: text alternatives, long descriptions, and data tables. Apply the same patterns to choropleths.https://www.w3.org/WAI/tutorials/images/complex/
- Official user guide for GeoPandas’ .plot() method, covering classification schemes, color maps, and missing-data handling used in this guide’s Python sample.https://geopandas.org/en/stable/docs/user_guide/mapping.html
- Official R-spatial vignette on plotting sf objects with base R and ggplot2 (geom_sf), including coordinate reference systems and projection guidance.https://r-spatial.github.io/sf/articles/sf5.html
- Maintained Observable notebook from the D3 team that mirrors the JavaScript code sample in this guide, including the legend and tooltip patterns.https://observablehq.com/@d3/choropleth/2
- The standard source of small- and medium-scale boundary data for world thematic maps. Free, public domain, and re-projection-friendly.https://www.naturalearthdata.com/