更改 geopandas 等值线地图图例的条目顺序

Changing the order of entries for a geopandas choropleth map legend

我正在城市地图上绘制某个分类值。我用来绘制的代码行如下:

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'}) 

其中城市数据是一个 geopandas 数据框,我使用 matplotlib 作为绘图库。参数 legend_kwds 允许我控制图例上的次要内容,例如位置或字体大小,但我无法决定主要内容,例如图例框中条目的顺序。事实上,我的类别是有排名的,比方说 1-2-3-4,但我总是让它们以不同的顺序显示。

是否可以对图例进行更多控制?例如,在 gdf.plot() 函数之外 调用它?而且,如果是这样,我如何将图例中的颜色与地图中的颜色相匹配,这些颜色是 viridis 颜色图的离散值(我不完全知道)?


编辑:这是一个可验证的例子。不幸的是shapefile需要其他文件才能工作,这里需要一个geometry(一个区域,而不是一个点)列,所以我不得不请你下载美国的this shpfile。您需要的一切都在这个文件夹中。这是重现问题的代码。输出的图不好,因为我不关心这里的坐标系统,但重要的是图例。

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)

假设您有 4 个图例,您可以按以下顺序设置它们。以下代码显示了如何按以下顺序(使用索引)放置它们:0, 2, 3, 1.

此处ax是您使用ax = plt.gca()

定义的轴对象
handles,labels = ax.get_legend_handles_labels()
handles = [handles[0], handles[2], handles[3], handles[1]]
labels = [labels[0], labels[2], labels[3], labels[1]]
ax.legend(handles, labels)

举个例子:

默认顺序

fig, ax = plt.subplots()

x = np.arange(5)
plt.plot(x, x, label=r'$y=x$')
plt.plot(x, 2*x, label=r'$y=2x$')
plt.plot(x, 3*x, label=r'$y=3x$')
plt.plot(x, 4*x, label=r'$y=4x$')
plt.legend(fontsize=16)    

手动更改顺序

fig, ax = plt.subplots()

x = np.arange(5)
plt.plot(x, x, label=r'$y=x$')
plt.plot(x, 2*x, label=r'$y=2x$')
plt.plot(x, 3*x, label=r'$y=3x$')
plt.plot(x, 4*x, label=r'$y=4x$')

handles,labels = ax.get_legend_handles_labels()
handles = [handles[0], handles[2],handles[3], handles[1]]
labels = [labels[0], labels[2], labels[3], labels[1]]
ax.legend(handles, labels, fontsize=16)

也可以使用预先指定的顺序列表作为列表推导式

order = [0, 2, 3, 1]
handles,labels = ax.get_legend_handles_labels()
handles = [handles[i] for i in order]
labels = [labels[i] for i in order]
ax.legend(handles, labels, fontsize=16)

坏消息是 geopandas 生成的图例中的类别是经过排序的,并且是硬编码的 (see source-code here)。

因此,一种解决方案是拥有分类列,这样如果它被排序,它将对应于所需的顺序。使用整数似乎很好。一旦以正确的顺序生成,就可以替换图例中的名称。

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()

我不得不稍微修改一下@ImportanceOfBeingErnest 接受的答案(函数中的第二行)以使其正常工作(也许此后有更新),

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.get_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()