How to Create a Choropleth Map in Plotly
Choropleth maps use color gradients to represent data values across geographic regions. They're ideal for visualizing how metrics vary by location—think election results by state, COVID-19 cases by...
Key Insights
- Plotly Express provides the fastest path to creating choropleth maps with just 5-10 lines of code, while Graph Objects offers granular control for complex visualizations
- Matching location identifiers correctly (FIPS codes, ISO codes, state names) is the most common source of errors when building choropleth maps
- Interactive features like hover tooltips and dropdown menus transform static geographic visualizations into powerful data exploration tools
Introduction to Choropleth Maps
Choropleth maps use color gradients to represent data values across geographic regions. They’re ideal for visualizing how metrics vary by location—think election results by state, COVID-19 cases by county, or sales performance by region. Unlike scatter plots on maps that show individual points, choropleth maps fill entire geographic boundaries with colors corresponding to data values.
Plotly stands out as the best choice for choropleth maps because it generates interactive HTML visualizations that work in Jupyter notebooks, web applications, and standalone HTML files. Users can hover over regions to see exact values, zoom into areas of interest, and pan across the map—all without writing JavaScript. The library handles the complex geographic calculations internally, letting you focus on your data story.
Setting Up Your Environment
Install Plotly and pandas if you haven’t already. You’ll need Plotly version 4.0 or higher to access the full choropleth functionality.
pip install plotly pandas
Here are the essential imports for creating choropleth maps:
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
# Check your Plotly version
import plotly
print(f"Plotly version: {plotly.__version__}")
Plotly Express (px) is your high-level interface for quick visualizations. Graph Objects (go) gives you low-level control when you need it. For most choropleth maps, you’ll start with Plotly Express and only reach for Graph Objects when customization demands it.
Preparing Geographic Data
Your data needs two components: location identifiers and values to visualize. Plotly recognizes several location identifier types:
- US States: Full names (“California”), abbreviations (“CA”), or FIPS codes
- US Counties: FIPS codes (5-digit strings like “06037” for Los Angeles County)
- Countries: ISO Alpha-3 codes (“USA”, “GBR”) or country names
- Custom regions: GeoJSON files with your own boundaries
The most critical step is ensuring your location identifiers match Plotly’s expectations. A mismatch here means blank maps or missing regions.
Let’s prepare sample data for US states:
# Sample data: unemployment rates by state
data = {
'state': ['California', 'Texas', 'Florida', 'New York', 'Pennsylvania',
'Illinois', 'Ohio', 'Georgia', 'North Carolina', 'Michigan'],
'unemployment_rate': [7.5, 6.8, 6.2, 8.1, 7.3, 7.9, 6.5, 6.1, 6.4, 8.3],
'population': [39.5, 29.1, 21.5, 19.8, 12.8, 12.7, 11.7, 10.6, 10.4, 10.0]
}
df = pd.DataFrame(data)
print(df.head())
For real-world applications, you’ll typically load data from CSV files or databases. Just ensure your location column contains consistent, valid identifiers.
Creating a Basic Choropleth with Plotly Express
Plotly Express makes creating a choropleth map remarkably simple. Here’s a complete working example:
import plotly.express as px
import pandas as pd
# Create sample data
df = pd.DataFrame({
'state': ['CA', 'TX', 'FL', 'NY', 'PA', 'IL', 'OH', 'GA', 'NC', 'MI',
'NJ', 'VA', 'WA', 'AZ', 'MA', 'TN', 'IN', 'MO', 'MD', 'WI'],
'value': [39.5, 29.1, 21.5, 19.8, 12.8, 12.7, 11.7, 10.6, 10.4, 10.0,
9.3, 8.6, 7.7, 7.3, 7.0, 6.9, 6.8, 6.2, 6.2, 5.9]
})
# Create choropleth map
fig = px.choropleth(
df,
locations='state',
locationmode='USA-states',
color='value',
scope='usa',
title='Population by State (millions)'
)
fig.show()
This seven-line example produces a fully interactive map. The locationmode='USA-states' parameter tells Plotly to interpret the locations as US state abbreviations. The scope='usa' parameter frames the map to show only the United States, removing other countries.
For international maps, the process is equally straightforward:
# World map example
world_df = pd.DataFrame({
'country': ['USA', 'CHN', 'IND', 'BRA', 'RUS', 'JPN', 'DEU', 'GBR'],
'gdp': [21.4, 14.3, 2.9, 1.8, 1.7, 5.1, 3.9, 2.8]
})
fig = px.choropleth(
world_df,
locations='country',
color='gdp',
hover_name='country',
title='GDP by Country (trillions USD)'
)
fig.show()
Customizing Your Choropleth Map
Basic maps work, but customization makes them professional. Here’s how to control colors, hover information, and map appearance:
import plotly.express as px
import pandas as pd
df = pd.DataFrame({
'state': ['CA', 'TX', 'FL', 'NY', 'PA', 'IL', 'OH', 'GA', 'NC', 'MI'],
'unemployment': [7.5, 6.8, 6.2, 8.1, 7.3, 7.9, 6.5, 6.1, 6.4, 8.3],
'population': [39.5, 29.1, 21.5, 19.8, 12.8, 12.7, 11.7, 10.6, 10.4, 10.0]
})
fig = px.choropleth(
df,
locations='state',
locationmode='USA-states',
color='unemployment',
scope='usa',
color_continuous_scale='Reds', # Color scheme
range_color=(5, 9), # Fix color scale range
hover_name='state',
hover_data={'unemployment': ':.1f', 'population': ':.1f'},
labels={'unemployment': 'Unemployment Rate (%)', 'population': 'Population (M)'},
title='US Unemployment Rates by State'
)
# Additional layout customization
fig.update_layout(
geo=dict(
lakecolor='rgb(255, 255, 255)',
projection_type='albers usa'
),
title_x=0.5, # Center the title
font=dict(size=14)
)
fig.show()
Key customization parameters:
color_continuous_scale: Choose from ‘Viridis’, ‘Reds’, ‘Blues’, ‘Greens’, or any Plotly color scalerange_color: Lock the color scale to specific min/max values for consistency across multiple mapshover_data: Format what appears in tooltips using Python format stringsprojection_type: Change map projections (’natural earth’, ‘orthographic’, ‘mercator’)
Advanced Techniques with Graph Objects
When you need complete control—custom GeoJSON boundaries, multiple data layers, or complex interactions—use Graph Objects:
import plotly.graph_objects as go
import pandas as pd
df = pd.DataFrame({
'state': ['CA', 'TX', 'FL', 'NY', 'PA'],
'value': [39.5, 29.1, 21.5, 19.8, 12.8]
})
fig = go.Figure(data=go.Choropleth(
locations=df['state'],
z=df['value'],
locationmode='USA-states',
colorscale='Blues',
colorbar=dict(
title="Population<br>(millions)",
thickness=15,
len=0.7,
x=0.9
),
marker_line_color='white',
marker_line_width=2
))
fig.update_layout(
title_text='Population by State - Graph Objects',
geo=dict(
scope='usa',
projection=go.layout.geo.Projection(type='albers usa'),
showlakes=True,
lakecolor='rgb(255, 255, 255)'
)
)
fig.show()
Graph Objects shine when working with custom GeoJSON files for regions not built into Plotly:
import json
# Load custom GeoJSON (example: custom sales territories)
with open('custom_regions.geojson', 'r') as f:
geojson = json.load(f)
fig = go.Figure(go.Choroplethmapbox(
geojson=geojson,
locations=df['region_id'],
z=df['sales'],
featureidkey="properties.id", # Key in GeoJSON properties
colorscale="Viridis",
marker_line_width=1
))
fig.update_layout(
mapbox_style="carto-positron",
mapbox_zoom=3,
mapbox_center={"lat": 37.0902, "lon": -95.7129}
)
fig.show()
Making It Interactive and Deploying
Add interactive controls to let users explore different dimensions of your data:
import plotly.graph_objects as go
import pandas as pd
# Sample data with multiple metrics
df = pd.DataFrame({
'state': ['CA', 'TX', 'FL', 'NY', 'PA', 'IL', 'OH', 'GA'],
'unemployment': [7.5, 6.8, 6.2, 8.1, 7.3, 7.9, 6.5, 6.1],
'income': [75235, 64034, 59227, 72108, 63463, 69187, 58642, 61224],
'education': [83.1, 83.7, 88.5, 86.7, 90.6, 89.0, 90.3, 86.4]
})
# Create figure with dropdown menu
fig = go.Figure()
# Add traces for each metric
for column in ['unemployment', 'income', 'education']:
fig.add_trace(go.Choropleth(
locations=df['state'],
z=df[column],
locationmode='USA-states',
colorscale='Viridis',
visible=(column == 'unemployment'), # Only first visible initially
name=column
))
# Create dropdown buttons
buttons = []
for i, column in enumerate(['unemployment', 'income', 'education']):
buttons.append(dict(
label=column.capitalize(),
method='update',
args=[{'visible': [j == i for j in range(len(df.columns)-1)]},
{'title': f'{column.capitalize()} by State'}]
))
fig.update_layout(
updatemenus=[dict(active=0, buttons=buttons, x=0.1, y=1.15)],
geo=dict(scope='usa'),
title='Select Metric'
)
fig.show()
For deployment, save your map as a standalone HTML file:
fig.write_html("choropleth_map.html")
This creates a fully self-contained file you can email, host on a web server, or embed in documentation. For dashboards, integrate with Dash (Plotly’s web framework) or embed in Streamlit applications.
Choropleth maps transform geographic data into visual insights. Start with Plotly Express for speed, customize with layout options for polish, and reach for Graph Objects when you need advanced control. The key is matching your location identifiers correctly—get that right, and the rest follows naturally.