使用geopandas绘制一个分级填充地图(choropleth map),并使用用户自定义的分类方案。

5

我对Python还比较陌生,所以我希望我的问题的答案相对简单明了。

我想使用geopandas制作一个choropleth地图。然而,由于我正在制作需要相互比较的多个地图,因此不可或缺的是使用自定义数据分类方案(而不是分位数或jenks)。因此,我一直在尝试使用User_Defined方案,并且我能够创建bins,但我不知道如何将它们应用到地图上。

这是我用来创建分类方案的方法:

    import pysal.esda.mapclassify as ps
    from pysal.esda.mapclassify import User_Defined

    bins = [5, 20, 100, 600, 1000, 3000, 5000, 10000, 20000, 400000]
    ud = User_Defined(projected_world_exports['Value'], bins)

(其中“Value”是我在地图中绘制的列)
然后,当我尝试绘制等值图时,我不知道该称呼哪种方案。
    projected_world_exports.plot(column='Value', cmap='Greens', scheme = ?????)

如果有人能帮忙,我将非常感激!
谢谢 x
3个回答

6

这里有一个替代方法,不需要修改geopandas代码。首先需要对区间进行标记,以便创建自定义的颜色映射表,将每个区间标签映射到特定的颜色。然后必须在geodataframe中创建一列,指定应将哪个区间标签应用于geodataframe中的每一行,然后使用此列来使用自定义颜色映射表绘制choropleth地图。

from matplotlib.colors import LinearSegmentedColormap

bins = [5, 20, 100, 600, 1000, 3000, 5000, 10000, 20000, 400000]

# Maps values to a bin.
# The mapped values must start at 0 and end at 1.
def bin_mapping(x):
    for idx, bound in enumerate(bins):
        if x < bound:
            return idx / (len(bins) - 1.0)

# Create the list of bin labels and the list of colors 
# corresponding to each bin
bin_labels = [idx / (len(bins) - 1.0) for idx in range(len(bins))]
color_list = ['#edf8fb', '#b2e2e2', '#66c2a4', '#2ca25f', '#006d2c', \
              '#fef0d9', '#fdcc8a', '#fc8d59', '#e34a33', '#b30000']

# Create the custom color map
cmap = LinearSegmentedColormap.from_list('mycmap', 
                                         [(lbl, color) for lbl, color in zip(bin_labels, color_list)])
projected_world_exports['Bin_Lbl'] = projected_world_exports['Value'].apply(bin_mapping)
projected_world_exports.plot(column='Bin_Lbl', cmap=cmap, alpha=1, vmin=0, vmax=1)

这似乎是为了一个相对简单的功能而付出了可怕的工作量。这方面有任何更新吗? - user5930691
是的,请检查我下面写的答案。链接:https://dev59.com/lZ7ha4cB1Zd3GeqPjGxa#66235068 - Suraj Regmi

5
我看了一下 geopandas 绘图函数的代码(https://github.com/geopandas/geopandas/blob/master/geopandas/plotting.py),但我想 plot 方法只接受三个名称之一("quantiles", "equal_interval", "fisher_jenks"),而不是直接接受一个 bins 列表或者像 User_Defined 这样的 pysal.esda.mapclassify 分类器。(我猜这可能与该问题有关,最后一条评论是关于定义“用户定义”分 binning 的 API。)
然而,目前来看,您可以通过轻微修改并重用我链接的文件中的函数来实现这一点。 例如,你可以像这样重新编写你自己的版本 plot_dataframe
import numpy as np

def plot_dataframe(s, column, binning, cmap,
                   linewidth=1.0, figsize=None, **color_kwds):
    import matplotlib.pyplot as plt

    values = s[column]
    values = np.array(binning.yb)

    fig, ax = plt.subplots(figsize=figsize)
    ax.set_aspect('equal')

    mn = values.min()
    mx = values.max()

    poly_idx = np.array(
        (s.geometry.type == 'Polygon') | (s.geometry.type == 'MultiPolygon'))
    polys = s.geometry[poly_idx]
    if not polys.empty:
        plot_polygon_collection(ax, polys, values[poly_idx], True,
                                vmin=mn, vmax=mx, cmap=cmap,
                                linewidth=linewidth, **color_kwds)

    plt.draw()
    return ax

接下来,您需要定义函数_flatten_multi_geomsplot_polygon_collection,通过复制它们并准备好像这样使用:

bins = [5, 20, 100, 600, 1000, 3000, 5000, 10000, 20000, 400000]
ud = User_Defined(projected_world_exports['Value'], bins)

plot_dataframe(projected_world_exports, 'Value', ud, 'Greens')

谢谢你的快速回答!我不知道_flatten_multi_geoms和polit_polygon_collection函数是什么,也不知道如何“复制”它们...另外,在第二行中,我是否应该将“binning”等同于什么?如果这些问题很简单,请原谅,像我这样在Python上经验很少。 - Chiara
关于你的第一个问题,我只是想举个例子,就是在你当前的工作文件/脚本或解释器中复制它们,以便当你使用 plot_dataframe 函数时它们已被定义。关于你的第二个问题,是的,参数(比如 binning)必须在调用 plot_dataframe 函数时定义;在实际调用时将会完成,例如:plot_dataframe(projected_world_exports, 'Value', ud, 'Greens'),与你的 geodataframe、字段名称等一起。 - mgc
很高兴它起作用了!(如果它对你有帮助,你可以考虑接受这个答案!) - mgc

3

使用用户定义方案可以轻松实现此操作。在定义这种方案时,将在幕后使用mapclassify.MapClassifier对象。实际上,所有支持的方案都由mapclassify提供。

要传递您的bins,需要在classification_kwds参数中传递它们。

因此,您的代码将是:

projected_world_exports.plot(
    column='Value', 
    cmap='Greens', 
    scheme='UserDefined', 
    classification_kwds={'bins': bins}
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接