Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing the order of entries for a geopandas choropleth map legend

I am plotting a certain categorical value over the map of a city. The line of code I use to plot is the following:

fig = plt.figure(figsize=(12, 12))
ax = plt.gca()
urban_data.plot(column="category", cmap="viridis", ax=ax, categorical=True, /
                k=4, legend=True, linewidth=0.5, /
                legend_kwds={'fontsize':'19', 'loc':'lower left'}) 

where urban data is a geopandas dataframe, and I am using matplotlib as plotting library. The argument legend_kwds allows me to control minor things on the legend, like the position or the font size, but I cannot decide major things like, for example, the order of the entries in the legend box. In fact my categories are ranked, let's say 1-2-3-4, but I always get them displayed in a different order.

Is it possible to have more control over the legend? For example by calling it outside the gdf.plot() function? And, if so, how do I match the colors in the legend with those in the map, which are discrete values (that I don't know exactly) of a viridis colormap?


EDIT: here is a verifiable example. Unfortunately shapefiles need other files to work, and here a geometry (an area, not a point) column is needed, so I have to ask you to download this shpfile of the US. Everything you need is within this folder. Here's the code to reproduce the issue. The plot in output is bad because I did not care about the coordinates system here, but the important thing is the legend.

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

gdf=gpd.read_file('.../USA_adm1.shp')
clusters=np.random.randint(0,4, size=52)
gdf['cluster']=clusters
clusdict={1: 'lower-middle', 2: 'upper-middle', 3: 'upper', 0: 'lower'}
gdf['cluster']=gdf['cluster'].map(clusdict)

fig = plt.figure(figsize=(12, 12))
ax = plt.gca()
gdf.plot(column='cluster',cmap='viridis', categorical=True, legend=True, ax=ax)
like image 599
sato Avatar asked Jan 28 '26 15:01

sato


1 Answers

The bad news is that categories in legends produced by geopandas are sorted and this is hardcoded (see source-code here).

One solution is hence to have the categorical column such that if it is sorted, it would correspond to the desired order. Using integers seems fine for that. Then one can replace the names in the legend, once it is produced in the correct order.

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

gdf=gpd.read_file('data/USA_adm/USA_adm1.shp')
clusters=np.random.randint(0,4, size=52)
gdf['cluster']=clusters
clusdict={1: 'lower-middle', 2: 'upper-middle', 3: 'upper', 0: 'lower'}

fig = plt.figure(figsize=(12, 12))
ax = plt.gca()
gdf.plot(column='cluster',cmap='viridis', categorical=True, legend=True, ax=ax)

def replace_legend_items(legend, mapping):
    for txt in legend.texts:
        for k,v in mapping.items():
            if txt.get_text() == str(k):
                txt.set_text(v)

replace_legend_items(ax.get_legend(), clusdict)

plt.show()

enter image description here

like image 60
ImportanceOfBeingErnest Avatar answered Jan 30 '26 04:01

ImportanceOfBeingErnest