14

I'm looking to plot a choropleth with a divergent colormap. I need to center the colormap in such a way that the middle color is displayed for a specific value (e.g. 0). How could I do this in GeoPandas/Geoplot?

I tried using a normalizer with geopandas.plot() but it's values are reset to data min and max once I plot:

import geopandas as gpd
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
world.loc[:,'random'] = np.random.normal(size=len(world))+2

norm = mpl.colors.Normalize(-4,4,clip=True)

fig, ax = plt.subplots()
world.plot('random', cmap=cmap, legend=True, norm=norm, ax=ax)

And here is the figure:

noncentered colors

1 Answer 1

13

You could normalize the color by using the TwoSlopeNorm function in matplotlib.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import TwoSlopeNorm
import geopandas as gpd

# generate data
gdf = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
gdf = gdf[gdf.continent == 'Africa']
gdf['random'] = np.random.gamma(2, 2, len(gdf)) - 2

# normalize color
vmin, vmax, vcenter = gdf.random.min(), gdf.random.max(), 0
norm = TwoSlopeNorm(vmin=vmin, vcenter=vcenter, vmax=vmax)
# create a normalized colorbar
cmap = 'RdBu'
cbar = plt.cm.ScalarMappable(norm=norm, cmap=cmap)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 7))
# with no normalization
gdf.plot(column='random', cmap=cmap, legend=True, ax=ax1)

# with normalization
gdf.plot(column='random', cmap=cmap, norm=norm, legend=False, ax=ax2)
# add colorbar
fig.colorbar(cbar, ax=ax2)

enter image description here

Notes:

  1. You could change vcenter to other desired values.
  2. You need to normalize the color for the map and create a new normalized colorbar. Otherwise, they won't match with one another.
  3. Set legend=False for the normalized map so that the unnormalized colorbar won't be plotted.
3
  • I wish I could upvote this more, great answer!
    – umbe1987
    Commented Jan 28, 2021 at 14:49
  • Great answer. However, the code does not seem up to date. Therefore, I posted a follow-up question.
    – Stücke
    Commented Nov 17, 2023 at 8:13
  • @Stücke I tested it on my end, and it appears to still be functioning. What problem are you experiencing?
    – steven
    Commented Nov 28, 2023 at 23:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.