【问题标题】:Changing the order of entries for a geopandas choropleth map legend更改 geopandas choropleth 地图图例的条目顺序
【发布时间】:2019-06-19 14:02:28
【问题描述】:

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

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 颜色图的离散值(我不确切知道)?


编辑:这是一个可验证的示例。不幸的是shapefiles需要其他文件才能工作,这里需要一个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)

【问题讨论】:

  • 当你说“我总是让它们以不同的顺序显示”时,这个顺序取决于什么?如果是数据框中出现的类别,您可能可以在绘图之前根据自己的喜好对其进行排序。
  • 我的猜测是整体按字母顺序排序(因为它们是),但我不知道如何控制它。在 gdf ​​中,它们都是混合的。
  • 我担心你需要在这里提供一些minimal reproducible example。也许this 可以根据您的情况进行调整?
  • @ImportanceOfBeingErnest 我添加了一个可验证的示例。不幸的是,有一个文件要下载,但之后很容易重现该问题。
  • 好的,太好了。您现在在图例中想要的顺序是什么?和数字0到3有关系吗?

标签: python matplotlib legend geopandas choropleth


【解决方案1】:

我不得不稍微更改@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()

【讨论】:

    【解决方案2】:

    坏消息是 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()
    

    【讨论】:

    • 好的,很酷。因此,如果所需的顺序不是 0-1-2-3(例如,bc,这些是从聚类算法输出的类别,不为它们分配排名目的),解决方案将是确定排名由我自己,然后应用此功能 2 次:第一次分配 0-1-2-3 标签排名明智,使图例成为排序整数的记录,然后再次给现在排序的所需名称标签。非常感谢!
    【解决方案3】:

    假设您有 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)
    

    【讨论】:

    • 我已经尝试过ax.get_legend_handles_labels(),但它返回的是空列表。可能是因为这里的图例不是ax属性,而是在plot函数中用legend=True调用的。但我不确定。唯一可以确定的是句柄和标签是空列表。
    • 嗯。在这种情况下,提供a Minimal, Complete, and Verifiable example
    • 嗯。这并不容易,因为我无法共享数据,而且创建“虚拟”地理数据框也不是立即的。一会儿我试试
    • 我添加了一个可验证的例子。
    猜你喜欢
    • 1970-01-01
    • 2015-10-23
    • 2016-12-30
    • 2017-12-20
    • 2021-07-06
    • 2020-10-18
    • 2023-01-16
    • 1970-01-01
    • 2014-05-09
    相关资源
    最近更新 更多