question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Plot chloropleth with consistent `legend` and bins

See original GitHub issue

How do I set a consistent colorscheme for three axes in the same figure?

The following should be a wholly reproducible example to run the code and get the same figure I have posted below.

Get the shapefile data from the Office for National Statistics. Run this in a terminal as a bash file / commands.

wget --output-document 'LA_authorities_boundaries.zip' 'https://opendata.arcgis.com/datasets/8edafbe3276d4b56aec60991cbddda50_1.zip?outSR=%7B%22latestWkid%22%3A27700%2C%22wkid%22%3A27700%7D&session=850489311.1553456889'

mkdir LA_authorities_boundaries
cd LA_authorities_boundaries
unzip ../LA_authorities_boundaries.zip

The python code that reads the shapefile and creates a dummy GeoDataFrame for reproducing the behaviour.

import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt

gdf = gpd.read_file(
    'LA_authorities_boundaries/Local_Authority_Districts_December_2015_Full_Extent_Boundaries_in_Great_Britain.shp'
)

# 380 values
df = pd.DataFrame([])
df['AREA_CODE'] = gdf.lad15cd.values
df['central_pop'] = np.random.normal(30, 15, size=(len(gdf.lad15cd.values)))
df['low_pop'] = np.random.normal(10, 15, size=(len(gdf.lad15cd.values)))
df['high_pop'] = np.random.normal(50, 15, size=(len(gdf.lad15cd.values)))

Join the shapefile from ONS and create a geopandas.GeoDataFrame

def join_df_to_shp(pd_df, gpd_gdf):
    """"""
    df_ = pd.merge(pd_df, gpd_gdf[['lad15cd','geometry']], left_on='AREA_CODE', right_on='lad15cd', how='left')

    # DROP the NI counties
    df_ = df_.dropna(subset=['geometry'])

    # convert back to a geopandas object (for ease of plotting etc.)
    crs = {'init': 'epsg:4326'}
    gdf_ = gpd.GeoDataFrame(df_, crs=crs, geometry='geometry')
    # remove the extra area_code column joined from gdf
    gdf_.drop('lad15cd',axis=1, inplace=True)

    return gdf_

pop_gdf = join_df_to_shp(df, gdf)

Make the plots

fig,(ax1,ax2,ax3,) = plt.subplots(1,3,figsize=(15,6))

pop_gdf.plot(
    column='low_pop', ax=ax1, legend=True,  scheme='quantiles', cmap='OrRd',
)
pop_gdf.plot(
    column='central_pop', ax=ax2, legend=True, scheme='quantiles', cmap='OrRd',
)
pop_gdf.plot(
    column='high_pop', ax=ax3, legend=True,  scheme='quantiles', cmap='OrRd',
)
for ax in (ax1,ax2,ax3,):
    ax.axis('off')

enter image description here

I want all three ax objects to share the same bins (preferable the central_pop scenario quantiles) so that the legend is consistent for the whole figure.

This way I should see darker colors (more red) in the far right ax showing the high_pop scenario.

How can I set the colorscheme bins for the whole figure / each of the ax objects?

The simplest way I can see this working is either a) Provide a set of bins to the geopandas.plot() function b) extract the colorscheme / bins from one ax and apply it to another.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:10 (1 by maintainers)

github_iconTop GitHub Comments

4reactions
knaaptimecommented, Jun 21, 2019
fig,(ax1,ax2,ax3,) = plt.subplots(1,3,figsize=(15,6))

bins = Quantiles(pop_gdf['central_pop'], 5).bins


pop_gdf.assign(cl=User_Defined(pop_gdf['low_pop'].dropna(), bins).yb).plot(
    column='cl', ax=ax1, cmap='OrRd'
)
pop_gdf.plot('central_pop', scheme='quantiles',  ax=ax2, cmap='OrRd', legend=True, cax=ax3,
             legend_kwds=dict(loc='upper right', bbox_to_anchor=(3.5, 0.75), title="Legend\n", frameon=False)

)
pop_gdf.assign(cl=User_Defined(pop_gdf['high_pop'].dropna(), list(bins)).yb).plot(
    column='cl', ax=ax3, cmap='OrRd', legend=False
)
for ax in (ax1,ax2,ax3,):
    ax.axis('off')

image

3reactions
knaaptimecommented, Jun 20, 2019

Under the hood, geopandas uses mapclassify, and the easiest way to achieve what you want would be to just use it directly:

import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt
from mapclassify import Quantiles, User_Defined

# Note you can read directly from the URL
gdf = gpd.read_file('https://opendata.arcgis.com/datasets/8edafbe3276d4b56aec60991cbddda50_1.zip?outSR=%7B%22latestWkid%22%3A27700%2C%22wkid%22%3A27700%7D&session=850489311.1553456889'
)

# 380 values
df = pd.DataFrame([])
df['AREA_CODE'] = gdf.lad15cd.values
df['central_pop'] = np.random.normal(30, 15, size=(len(gdf.lad15cd.values)))
df['low_pop'] = np.random.normal(10, 15, size=(len(gdf.lad15cd.values)))
df['high_pop'] = np.random.normal(50, 15, size=(len(gdf.lad15cd.values)))

def join_df_to_shp(pd_df, gpd_gdf):
    """"""
    df_ = pd.merge(pd_df, gpd_gdf[['lad15cd','geometry']], left_on='AREA_CODE', right_on='lad15cd', how='left')

    # DROP the NI counties
    df_ = df_.dropna(subset=['geometry'])

    # convert back to a geopandas object (for ease of plotting etc.)
    crs = {'init': 'epsg:4326'}
    gdf_ = gpd.GeoDataFrame(df_, crs=crs, geometry='geometry')
    # remove the extra area_code column joined from gdf
    gdf_.drop('lad15cd',axis=1, inplace=True)

    return gdf_

pop_gdf = join_df_to_shp(df, gdf)

fig,(ax1,ax2,ax3,) = plt.subplots(1,3,figsize=(15,6))

# define your bins
bins = Quantiles(pop_gdf['central_pop'], 5).bins

# create a new column with the discretized values and plot that col
# repeat for each view
pop_gdf.assign(cl=User_Defined(df['low_pop'].dropna(), bins).yb).plot(
    column='cl', ax=ax1, cmap='OrRd'
)
pop_gdf.assign(cl=User_Defined(df['central_pop'].dropna(), bins).yb).plot(
    column='cl', ax=ax2, cmap='OrRd',
)
pop_gdf.assign(cl=User_Defined(df['high_pop'].dropna(), list(bins)).yb).plot(
    column='cl', ax=ax3, cmap='OrRd',
)
for ax in (ax1,ax2,ax3,):
    ax.axis('off')

image

Read more comments on GitHub >

github_iconTop Results From Across the Web

Choropleth maps in Python - Plotly
A Choropleth Map is a map composed of colored polygons. It is used to represent spatial variations of a quantity. This page documents...
Read more >
Python Figure Reference: layout - Plotly
Determines if the legend items symbols scale with their corresponding "trace" attributes or remain "constant" independent of the symbol size on the graph....
Read more >
Legends in Python - Plotly
In this example itemsizing attribute determines the legend items symbols remain constant, regardless of how tiny/huge the bubbles would be in the graph....
Read more >
Is it possible to manually label your legend in px.choropleth ...
So, I am making a standard choropleth graph in plotly.express with the following code: import psycopg2 import pandas as pd import ...
Read more >
Python Figure Reference: choropleth Traces - Plotly
A plotly.graph_objects.Choropleth trace is a graph object in the figure's data list with any of the named arguments or attributes listed below.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found