- 数据分析
- GIS
【免费下载链接】geopandas
Python tools for geographic data
分级统计图(Choropleth Map)是地理可视化中最常用的表达方式之一:它用每个面状要素的颜色来代表某个统计变量的取值,从而把"数值分布"直接映射到"空间分布"。GeoPandas 的GeoDataFrame.plot()原生支持这一能力——只需把包含目标数值的列名传给column参数即可出图;在此基础上,还可以借助mapclassify生态获得等分位、自然间断点等专业制图分类方案,并通过vmin/vmax、legend_kwds、classification_kwds等参数精细控制配色、图例与分箱。读完本文,你将掌握用 GeoPandas 从"默认连续配色"到"专业分类制图"的完整实战路径,并理解这些参数在 plotting.py 底层是如何被解析和执行的。
本文主体对应仓库文档 doc/source/docs/user_guide/choropleth.rst,源码佐证来自 geopandas/plotting.py 与 geopandas/tests/test_plotting.py。
什么是分级统计图,如何用 GeoPandas 生成
分级统计图用颜色表示一个连续型或有序型变量在每个几何对象上的取值。GeoPandas 原生支持这一功能:在GeoDataFrame.plot()方法中传入column参数,指定包含该变量的列名即可。其调用链是:GeoDataFrame.plot实际是由 geodataframe.py 中注册的GeoplotAccessor访问器提供的,__call__在kind="geo"时把任务转交给 plotting.py 中的plot_dataframe()函数;GeoSeries.plot则直接调用plot_series()(见 geoseries.py)。
先加载示例数据。文档使用geodatasets提供的数据集,其中包含美国南部各州县级区划及其社会经济统计属性:
import geopandas import geodatasets south = geopandas.read_file( geodatasets.get_path("geoda.south") )该数据集为每个县关联了多项社会人口学指标。下面以1990 年女性户主家庭比例(列名"FH90")为例展开讲解。
默认行为:连续线性配色与图例
不指定任何额外选项,直接把列名传给column:
south.plot(column='FH90');此时 GeoPandas 会把该列的最小值到最大值之间的取值区间,线性地映射到默认色带(源码默认使用viridis)上,值越大颜色越深,形成一张基础的分级统计图。
如果需要图例,加上legend=True:
south.plot(column='FH90', legend=True);默认情况下,legend=True会生成一个colorbar(色条)。从源码看(plotting.py),连续型数据的图例是通过ScalarMappable+ax.figure.colorbar()绘制的,因此外观由 matplotlib 的 colorbar 规范控制。
用 vmin / vmax 定制颜色范围
默认的色带映射会从"观测值的最小值"拉伸到"观测值的最大值"。如果希望固定颜色映射的上下界,可以使用vmin和vmax:
south.plot(column='FH90', legend=True, vmax=30);上例把颜色上限锁定为 30,那些FH90超过 30 的县不再参与色带拉伸。此时 colorbar 会在两端出现箭头形延伸标记,提示数据中存在超出色带范围的值——这是 GeoPandas 自动处理的:当mn > values_min或mx < values_max时,extend会被自动设置为"min"、"max"或"both"(见 plotting.py)。
在vmin/vmax与分类方案(scheme)组合使用时,这两个参数还会先过滤数据再参与分箱:源码中vmin/vmax会作为掩码剔除范围之外的值,再交给分类器计算分箱边界(plotting.py)。
分类方案(scheme):从连续映射到离散分箱
默认的连续线性映射未必是最佳制图方案——真实数据往往高度偏斜,线性映射会把大部分颜色浪费在少数极端值上。此时可以用scheme参数指定分类方案,把数值离散化到若干颜色分箱中。例如使用"quantiles"(分位数),每个分箱包含数量相同的几何要素:
south.plot(column='FH90', legend=True, scheme='quantiles');依赖提示:使用
scheme需要安装可选依赖mapclassify(pip install mapclassify或conda install -c conda-forge mapclassify)。源码在运行时才导入该包(plotting.py),若未安装会抛出ImportError,提示信息为"The 'mapclassify' package is required to use the 'scheme' keyword."。
分类图例 vs colorbar:legend_kwds 中的 colorbar 开关
设置scheme后,默认的图例样式也会随之改变——从连续的 colorbar 变成分类式图例(每个分箱一个色块)。这一默认行为是历史遗留的,它本身也有用处;但更常见的需求是保留 colorbar。可以通过legend_kwds显式请求:
south.plot( column='FH90', legend=True, scheme='quantiles', legend_kwds={"colorbar": True}, );从源码看,legend_kwds中的colorbar键被pop出来后,会决定后续走哪条渲染路径(plotting.py):
colorbar=False(默认):强制按分类变量处理,逐组调用plot_series绘制并生成分类图例;colorbar=True:走连续路径,用matplotlib.colors.BoundaryNorm按分箱边界建立分段色带,再绘制 colorbar(plotting.py)。
色条内颜色的间距:proportional 与 uniform
使用 colorbar 图例时,颜色在色条上的分布有两种方式:
- proportional(按比例,默认):每个分箱占据的色条长度与其代表的数值区间宽度成正比;
- uniform(均匀):每个分箱占据等长色条空间。
当分箱边界在数值上密集地挤在一起时(例如大量小值集中分布),proportional 方式会让这些分箱在色条上挤成一团、难以辨认;此时改用 uniform 能让每种颜色获得足够的展示空间:
south.plot( column='FH90', legend=True, scheme='quantiles', legend_kwds={"colorbar": True, "spacing": "uniform"}, );底层逻辑位于 plotting.py:使用scheme且未显式指定spacing时,默认设为"proportional",用户可通过legend_kwds覆盖。
控制分箱数量:k 参数
大多数分类方案支持k参数,用于指定分箱(类别)数量,默认值为 5(见 plotting.py 与 plotting.py,该值会注入classification_kwds["k"])。在使用 colorbar 图例时,建议使用更大的k值,以获得更平滑的色带过渡:
south.plot( column='FH90', legend=True, scheme='quantiles', k=10, legend_kwds={"colorbar": True}, );mapclassify 支持的全部方案
分类方案由mapclassify包提供,GeoPandas 支持其全部内置方案。源码 docstring(plotting.py)列出的当前可用方案包括:
"BoxPlot"— 箱线图间断"EqualInterval"— 等间隔"FisherJenks"— Fisher-Jenks 自然间断(最优)"FisherJenksSampled"— Fisher-Jenks 抽样版(适合大数据)"HeadTailBreaks"— 头尾分割"JenksCaspall"— Jenks-Caspall 法"JenksCaspallForced"— 强制 Jenks-Caspall"JenksCaspallSampled"— 抽样 Jenks-Caspall"MaxP"— 最大 p 值法"MaximumBreaks"— 最大间断法"NaturalBreaks"— 自然间断"Percentiles"— 百分位数"PrettyBreaks"— 美观间断"Quantiles"— 分位数"StdMean"— 均值-标准差"UserDefined"— 用户自定义边界
不同的方案会强调同一数据中的不同空间模式。例如把四种常用方案放在同一画布上对比:
import matplotlib.pyplot as plt fig, axs = plt.subplots(2, 2) schemes = ["EqualInterval", "Quantiles", "NaturalBreaks", "FisherJenks"] for scheme, ax in zip(schemes, axs.flat): south.plot( column="FH90", scheme=scheme, legend=True, legend_kwds={"colorbar": True}, ax=ax) ax.set_title(scheme) ax.set_axis_off() plt.tight_layout()从测试用例 test_plotting.py 可以看出,GeoPandas 的测试会遍历 mapclassify 的全部分类器逐一验证可绘制性;同时方案名称兼容带下划线和不带下划线的写法(如"FISHER_JENKS"与"FISHERJENKS",见 test_plotting.py)。
拓扑着色:scheme="greedy"
除了对属性值分类,GeoPandas 还支持scheme="greedy",它调用mapclassify.greedy()进行拓扑(贪心)着色:用尽可能少的颜色给相邻要素着色,保证相邻要素颜色互不相同。因为它不是对属性值分类,所以不能与column同时使用:
south.plot(scheme="greedy", cmap="Set3");源码中的对应分支(plotting.py)会先校验column必须为None,否则抛出ValueError: The scheme='greedy' cannot be specified together with column.;随后把mapclassify.greedy(df)的结果包装成pd.Categorical作为着色依据,并以categorical=True走分类绘制路径。
向分类器传递参数:classification_kwds
分类器自身的参数可以通过classification_kwds以字典形式传入。例如"UserDefined"方案需要用户提供自定义分箱边界bins和最低值lowest:
south.plot( column="FH90", legend=True, scheme="UserDefined", classification_kwds={ "bins": [10, 20, 30], "lowest": 0 }, legend_kwds={"colorbar": True}, );各方案支持的具体参数需查阅 mapclassify 的文档;在 GeoPandas 侧,classification_kwds会原样展开为mapclassify.classify(values, scheme, **classification_kwds)的关键字参数(plotting.py)。
图例的细粒度控制:fmt 与 interval
当使用分类式图例(即未开启colorbar)时,legend_kwds还支持两个专属选项(plotting.py):
fmt:分箱边界标签的数字格式化字符串。例如{"fmt": "{:.0f}"}可去掉小数位;interval:布尔值,默认False。设为True时,图例标签会显示开闭区间括号(如( 3.30, 10.00])。
这两项由 plotting.py 中binning.get_legend_classes()的调用处理:fmt控制边界格式化,interval=False时会把生成的区间字符串首尾的方括号字符去掉。对应测试见 test_plotting.py。
自定义配色:Colormaps
分级统计图的配色可以用cmap参数控制,其取值可以是:
- 任意 matplotlib 能识别的色带名称字符串(如
"viridis"、"cividis"、"plasma"); - 任意
matplotlib.colors.Colormap对象; - 对于分类绘制(categorical),还可以是
value -> color的字典映射(见 plotting.py)。
对比多个色带的效果:
fig, axs = plt.subplots(2, 2, sharex=True, sharey=True) cmaps = ["viridis", "cividis", "plasma", "managua"] for cmap, ax in zip(cmaps, axs.flat): south.plot(column='FH90', cmap=cmap, ax=ax, add_labels=False) ax.set_title(cmap) plt.tight_layout()值得注意的源码细节(plotting.py):分类绘制时若未指定cmap,GeoPandas 会根据分组数量自动选择——设置了scheme时默认用viridis,否则分组数超过 10 用tab20、不超过 10 用tab10;对于N < 32的分类色带(如tab10)按索引逐色取值,对于连续色带则按比例在整条色带上取值。
源码视角:完整的分级统计图执行流程
把上述参数串起来,plot_dataframe()在 plotting.py 中的执行流程可以概括为:
- 依赖检查:设置了
scheme时尝试导入mapclassify,缺失则抛出ImportError(L929-L935); - greedy 分支:
scheme="greedy"校验不与column共用,转为分类着色(L937-L947); - 数据准备:解析
column(列名或 array-like),推断是否分类变量;拷贝legend_kwds避免原地修改(L985-L1022); - 分箱计算:若设置
scheme,把k(默认 5)注入classification_kwds,用vmin/vmax构造掩码,调用mapclassify.classify()得到分箱对象binning(L1026-L1036); - 图例模式分流:未开
colorbar则转为分类绘制并生成标签;开启colorbar则用BoundaryNorm建立分段归一化,并默认spacing="proportional"(L1038-L1059, L1172-L1195); - 分类绘制:按
groupby分组逐组调用plot_series,自定义标签数量与分组数不符时抛出ValueError(L1064-L1162); - 连续绘制:按几何类型(多边形/线/点)分三个集合绘制,并自动处理
extend、colorbarshrink等图例细节(L1197-L1305)。
对应的测试类TestMapclassifyPlotting(test_plotting.py)覆盖了图例标签、colorbar 刻度位置、自定义标签、负值数据、fmt/interval、方案名兼容性、classification_kwds传参等场景,是学习各参数组合行为的极佳参考。
小结
GeoPandas 的分级统计图能力以column参数为入口,形成了一条完整的可调链路:
| 需求 | 参数 |
|---|---|
| 指定着色变量 | column |
| 显示图例(默认 colorbar) | legend=True |
| 固定颜色映射范围 | vmin/vmax |
| 启用离散分类方案 | scheme(需安装mapclassify) |
| 分箱数量 | k(默认 5) |
| 分类器参数 | classification_kwds |
| 图例样式(colorbar / 分类图例、间距、格式化) | legend_kwds |
| 自定义配色 | cmap |
无论是快速出图的默认连续配色,还是面向专业制图的等分位、Fisher-Jenks 自然间断与自定义分箱,都可以通过这几个参数在GeoDataFrame.plot()上一次完成。想深入了解底层实现,可直接阅读 geopandas/plotting.py 中的plot_dataframe(),并结合 geopandas/tests/test_plotting.py 中的TestMapclassifyPlotting测试类验证各参数组合的实际行为。
- 数据分析
- GIS
【免费下载链接】geopandas
Python tools for geographic data
相关推荐
Go 语言 map 与结构体双向解码实战:Tekton Pipeline 依赖的 mapstructure 库全解析
Go 语言 map 与结构体双向解码实战:Tekton Pipeline 依赖的 mapstructure 库全解析 mapstructure 是 Go 生态中
数据分析GISLeaflet 交互式分级统计图(Choropleth Map)实战:以美国州级人口密度为例
Leaflet 交互式分级统计图(Choropleth Map)实战:以美国州级人口密度为例 本篇技术指南以 Leaflet 官方教程《Interactive
前端数据可视化GIS用 Vega 绘制 Box Plot 箱线图:从 aggregate 分位数统计到分面分组的完整实战
用 Vega 绘制 Box Plot 箱线图:从 aggregate 分位数统计到分面分组的完整实战 箱线图(Box Plot)是一种用一组汇总统计量概括定量值
数据可视化
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考