Choropleth maps in plotly

The choropleth_map function from plotly

When using plotly and Python, you can make use of the choropleth_map function from plotly express in order to create choropleth maps over a tile map. The function is a bit complex at first glance, as you will need to input the geometries and there are several ways to do it. The most common are using a geojson for the geometries and a data frame to relate each geometry with a value and the other is to use a shapefile with all the data stored (geometries and values).

Data from a geojson

If your geometries are stored in a geojson file you will need to import them and you will also need to import the data frame with at least an identifier and the values. After importing your data, you will need to input the data frame to data_frame and the geometry to geojson. You will also need to set featureidkey to the geojson identifier (defaults to id and in other case takes the form properties.<key>) which matches the column name passed to locations. Finally, you will need to input the name of the column of the data frame representing values to color.

The other arguments are used to set the map style, the center point of the map and the zoom level.

from urllib.request import urlopen
import json
import pandas as pd
import plotly.express as px

# Geojson
with urlopen('https://github.com/R-CoderDotCom/data/blob/main/shapefile_spain/spain.geojson?raw=true') as response:
    ccaa = json.load(response)

# Data
unemp_rates = pd.read_csv("https://raw.githubusercontent.com/R-CoderDotCom/data/main/shapefile_spain/unemployment_rates.csv",
                   dtype = {"ccaa_id": str}, encoding = 'latin-1')

fig = px.choropleth_map(
    data_frame = unemp_rates,            # Data frame with values
    geojson = ccaa,                      # Geojson with geometries
    featureidkey = 'properties.ccaa_id', # Geojson key which relates the file with the data from the data frame
    locations = 'ccaa_id',               # Name of the column of the data frame that matches featureidkey
    color = 'unemp_rate',                # Name of the column of the data frame with the data to be represented
    map_style = 'open-street-map',
    center = dict(lat = 40.0, lon = -3.72),
    zoom = 4)

fig.show()

Choropleth map in Python with plotly from a geojson and a data frame

Since plotly 5.24 the tile map functions (scatter_map, density_map, choropleth_map…) are based on MapLibre and don’t need any token. They replace the old *_mapbox functions, which were deprecated and removed in plotly 7. The available styles for map_style are ‘basic’ (default), ‘streets’, ‘outdoors’, ‘light’, ‘dark’, ‘satellite’, ‘satellite-streets’, ‘open-street-map’, ‘white-bg’, ‘carto-positron’, ‘carto-darkmatter’ and ‘carto-voyager’ (the last three also have a version without labels, such as ‘carto-positron-nolabels’).

Data from a shapefile

If your data is from a shapefile you will need to read it with geopandas or another similar library. Then, you will need to input your data as in the example below.

import geopandas
import plotly.express as px

# Shapefile data
shp = geopandas.read_file("https://github.com/R-CoderDotCom/data/blob/main/shapefile_spain/spain.zip?raw=true")
shp = shp.to_crs("WGS84")

fig = px.choropleth_map(
    data_frame = shp.set_index("ccaa_id"), # Using the id as index of the data
    geojson = shp.geometry,                # The geometry
    locations = shp.index,                 # The index of the data
    color = 'unemp_rate',
    map_style = 'open-street-map',
    center = dict(lat = 40.0, lon = -3.72),
    zoom = 4)

fig.show()

Choropleth map in plotly from a shapefile and geopandas

Hover

If you hover over each region of Spain you can read the identifier and the unemployment rate. However, you can also pass the column name of a variable to hover_name, so that variable will also appear on hover. In the following example we are passing the name of each autonomous community.

from urllib.request import urlopen
import json
import pandas as pd
import plotly.express as px

# Geojson
with urlopen('https://github.com/R-CoderDotCom/data/blob/main/shapefile_spain/spain.geojson?raw=true') as response:
    ccaa = json.load(response)

# Data
unemp_rates = pd.read_csv("https://raw.githubusercontent.com/R-CoderDotCom/data/main/shapefile_spain/unemployment_rates.csv",
                   dtype = {"ccaa_id": str}, encoding = 'latin-1')

fig = px.choropleth_map(
    data_frame = unemp_rates,
    geojson = ccaa,
    featureidkey = 'properties.ccaa_id',
    locations = 'ccaa_id',
    color = 'unemp_rate',
    hover_name = 'name',
    map_style = 'open-street-map',
    center = dict(lat = 40.0, lon = -3.72),
    zoom = 4)

fig.show()

Text on hover of a plotly choropleth map

Color palette

It is possible to customize the color palette used for the choropleths passing a list of colors or a plotly color palette to color_continuous_scale. Note that you can also customize the level of transparency with opacity, which will let you see what’s behind.

from urllib.request import urlopen
import json
import pandas as pd
import plotly.express as px

# Geojson
with urlopen('https://github.com/R-CoderDotCom/data/blob/main/shapefile_spain/spain.geojson?raw=true') as response:
    ccaa = json.load(response)

# Data
unemp_rates = pd.read_csv("https://raw.githubusercontent.com/R-CoderDotCom/data/main/shapefile_spain/unemployment_rates.csv",
                   dtype = {"ccaa_id": str}, encoding = 'latin-1')

fig = px.choropleth_map(
    data_frame = unemp_rates,
    geojson = ccaa,
    featureidkey = 'properties.ccaa_id',
    locations = 'ccaa_id',
    color = 'unemp_rate',
    color_continuous_scale = 'viridis',
    opacity = 0.5,
    map_style = 'open-street-map',
    center = dict(lat = 40.0, lon = -3.72),
    zoom = 4)

fig.show()

Color palette of a plotly Python choropleth map

Borders style

By default, the geometries borders have a grayish color. Nonetheless, you can override the color and even the width of the lines with the marker_line_width and marker_line_color arguments from the update_traces function.

from urllib.request import urlopen
import json
import pandas as pd
import plotly.express as px

# Geojson
with urlopen('https://github.com/R-CoderDotCom/data/blob/main/shapefile_spain/spain.geojson?raw=true') as response:
    ccaa = json.load(response)

# Data
unemp_rates = pd.read_csv("https://raw.githubusercontent.com/R-CoderDotCom/data/main/shapefile_spain/unemployment_rates.csv",
                   dtype = {"ccaa_id": str}, encoding = 'latin-1')

fig = px.choropleth_map(
    data_frame = unemp_rates,
    geojson = ccaa,
    featureidkey = 'properties.ccaa_id',
    locations = 'ccaa_id',
    color = 'unemp_rate',
    color_continuous_scale = 'viridis',
    map_style = 'open-street-map',
    center = dict(lat = 40.0, lon = -3.72),
    zoom = 4)

fig.update_traces(marker_line_width = 2, marker_line_color = 'white')

fig.show()

Border color of the polygons of a Python choropleth map

Map style

You can change the base map with the map_style argument. There are several map styles available.

from urllib.request import urlopen
import json
import pandas as pd
import plotly.express as px

# Geojson
with urlopen('https://github.com/R-CoderDotCom/data/blob/main/shapefile_spain/spain.geojson?raw=true') as response:
    ccaa = json.load(response)

# Data
unemp_rates = pd.read_csv("https://raw.githubusercontent.com/R-CoderDotCom/data/main/shapefile_spain/unemployment_rates.csv",
                   dtype = {"ccaa_id": str}, encoding = 'latin-1')

fig = px.choropleth_map(
    data_frame = unemp_rates,
    geojson = ccaa,
    featureidkey = 'properties.ccaa_id',
    locations = 'ccaa_id',
    color = 'unemp_rate',
    color_continuous_scale = 'viridis',
    map_style = 'outdoors',
    center = dict(lat = 40.0, lon = -3.72),
    zoom = 4)

fig.show()

Map style of a choropleth map in Python with choropleth_map

The choropleth function

The choropleth function from plotly express is an alternative to choropleth_map which doesn’t use tile maps. Both functions are very similar but when using choropleth you will need to set a scope (by default, scope = 'world') and you will need to pick the zoom level with update_layout. Note that you can also set a cartographic projection with projection, whose default value depends on the selected scope. You will find here the available scopes and projections.

The choropleth function from plotly express

from urllib.request import urlopen
import json
import pandas as pd
import plotly.express as px

# Geojson
with urlopen('https://github.com/R-CoderDotCom/data/blob/main/shapefile_spain/spain.geojson?raw=true') as response:
    ccaa = json.load(response)

# Data
unemp_rates = pd.read_csv("https://raw.githubusercontent.com/R-CoderDotCom/data/main/shapefile_spain/unemployment_rates.csv",
                   dtype = {"ccaa_id": str}, encoding = 'latin-1')

fig = px.choropleth(
    data_frame = unemp_rates,
    geojson = ccaa,
    featureidkey = 'properties.ccaa_id',
    locations = 'ccaa_id',
    color = 'unemp_rate',
    color_continuous_scale = 'viridis',
    scope = 'europe',
    center = dict(lat = 40.0, lon = -3.72))

fig.update_layout(autosize = True, geo = dict(projection_scale = 5))

fig.show()

Base map visibility

Note that this function allows hiding the base map setting basemap_visible = False.

Visibility of the base map of a Python choropleth map

from urllib.request import urlopen
import json
import pandas as pd
import plotly.express as px

# Geojson
with urlopen('https://github.com/R-CoderDotCom/data/blob/main/shapefile_spain/spain.geojson?raw=true') as response:
    ccaa = json.load(response)

# Data
unemp_rates = pd.read_csv("https://raw.githubusercontent.com/R-CoderDotCom/data/main/shapefile_spain/unemployment_rates.csv",
                   dtype = {"ccaa_id": str}, encoding = 'latin-1')

fig = px.choropleth(
    data_frame = unemp_rates,
    geojson = ccaa,
    featureidkey = 'properties.ccaa_id',
    locations = 'ccaa_id',
    color = 'unemp_rate',
    color_continuous_scale = 'viridis',
    scope = 'europe',
    center = dict(lat = 40.0, lon = -3.72),
    basemap_visible = False)

fig.update_layout(autosize = True, geo = dict(projection_scale = 5))

fig.show()
Storytelling with Data

A Data Visualization Guide for Business Professionals

Buy on Amazon

See also