How to fix the scale of several choropleth maps?

Question:

I am creating multiple choropleth maps for different time frames (because I want to do a gif) and wondering if there is any way to fix the scale for each map to do easy visual comparison ?

My current code is as follows

years = range(2000, 2020)

# Create the images for each year
images = []
for year in years:
    fig, ax = plt.subplots(figsize=(10, 10))
    agg.plot(column="nac_"+str(year), cmap='Blues', ax=ax, legend=True, legend_kwds={'label': f'Births in {year}'})
    ax.set_title(f'Number of births in {year}')
    ax.set_axis_off()
    plt.axis('equal')
    plt.tight_layout()
    plt.savefig(f'births_{year}.png', dpi=150)
    plt.close()
    images.append(imageio.imread(f'births_{year}.png'))

# Save the images as a gif
imageio.mimsave('births.gif', images, fps=4)

The ouput looks like this

neededthis

From looking at it, is clear that since the limits of the color scale changes, I can’t really observe how districts decline in the number of births.

Answers:

Here is a demo code that demonstrates the use of normalized colorbars for the thematic maps. Read comments in the code for more explanation.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from matplotlib import colorbar, colors
import cartopy.crs as ccrs
import cartopy.feature as cf

X   = [9.95, 11.06, 12.18, 9.13, 8.40, 
       11.08, 11.05, 9.86, 12.50, 7.83, 
       10.93, 11.81, 10.02, 8.54, 10.29] 

Y   = [49.74, 47.43, 49.62, 47.62, 48.48, 
       47.09, 49.53, 48.56, 48.29, 48.02, 
       49.15, 48.34, 50.22, 49.53, 47.34]

# Base values of thematic data
Z   = np.array([2.33, -0.52, 0.97, 
       -0.50,  0.21, 0.40,
       -1.69, 1.37, -1.79, 
       -0.09, 0.96, 0.12, 
       -0.03,  1.36, -0.37])

# Data statistics
zmin, zmax = min(Z), max(Z)
zavg = 0.5 + (zmin+zmax)/2

# Make-up more data sets for demo purposes
Z1 = [Z, 8+Z, Z-4]
Z2 = [9*Z, zavg+4*Z, -5*Z-zavg]

# Here I get 6 data sets for 2x3 subplots
ZZ = [Z1, Z2]

# Compute values for `norm`
#  vmin: minimum of all possible values from all data sets
#  vmax: maximum of all possible values ...
vmin = min(np.array(ZZ).flat)
vmax = max(np.array(ZZ).flat)

# Finally, compute norm
mynorm = mpl.colors.Normalize(vmin=vmin, vmax=vmax)
# Choose a colormap
mycmap = "plasma"  #viridis

# visualization
numCol, numRow = 3, 2
fig,ax = plt.subplots(ncols=numCol, nrows=numRow, figsize=(13,5),
                      subplot_kw={'projection': ccrs.PlateCarree()})

fig.tight_layout()

# This plots 2x3 array of maps
# Each map has the same colorbar for its thematic representation
for col in range(numCol):
    for row in range(numRow):
        ax[row,col].set_extent([7.2, 13.7, 46.8, 50.7])
        ax[row,col].add_feature(cf.BORDERS, linewidth=0.3)
        ax[row,col].set_aspect('equal')
        ax[row,col].set_title("Row/Col: "+str(row)+"|"+str(col))
        mmin, mmax = min(ZZ[row][col]), max(ZZ[row][col])
        print(f"Row:{row}, col:{col}, minVal:{mmin:.2f}, maxVal:{mmax:.2f}")
        ax[row,col].scatter(X, Y, c=ZZ[row][col], s=100,
                        cmap = mycmap,
                        norm = mynorm, 
                        edgecolors='none',
                        transform=ccrs.PlateCarree())
        fig.colorbar(mpl.cm.ScalarMappable(norm=mynorm, cmap=mycmap),
                ax=ax[row,col],
                aspect = 12,
                shrink=0.79)

Output array of maps:

2x3maps

Note that each of the 6 data sets has different min/max values. Without using normalized colormap, the colorbars of them will be different.

The code above should give the idea for you to adapt to your use case.

Since all the colorbars are the same, in practice you only need to plot 1 of them.

Answered By: swatchai
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.