
本文介绍如何在 pandas 中按 `year` 和 `region` 分组计算 `var` 的平均值,并为每个区域内的上下边界点(`lower`/`upper`)分配环状顺序索引(`loop`),以支持后续多边形(如带状图、置信区间填充)的顶点连接。
在地理可视化、时间序列置信带或分组区域填充图中,常需将离散的上下界数据(如 lower/upper)按逻辑顺序首尾相连构成闭合多边形。这要求:
- 均值列:对每组 (year, region) 内所有 var 值求平均(用于标注或基准线);
- 环状索引列(loop):在每个 region 内,将 lower 组按 year 升序排列、upper 组按 year 降序排列,再统一编号 0, 1, 2, ...,从而形成“下起点→下终点→上终点→上起点”的闭环路径。
以下为完整实现代码(基于 pandas 1.5+):
import pandas as pd
# 原始数据
mydict = {
'year': [2010, 2010, 2011, 2011, 2010, 2010, 2011, 2011],
'region': [1, 1, 1, 1, 2, 2, 2, 2],
'group': ['lower', 'upper', 'lower', 'upper', 'lower', 'upper', 'lower', 'upper'],
'var': [10, 20, 30, 40, 50, 60, 70, 80]
}
df = pd.DataFrame(mydict)
# 步骤1:添加 average 列 —— 按 (year, region) 分组求 var 均值
df['average'] = df.groupby(['year', 'region'])['var'].transform('mean')
# 步骤2:添加 loop 列 —— 构建环状排序逻辑
df['loop'] = (
df.assign(
# 对 upper 组的 var 取负,使排序时 upper 行“反向”插入
sort_key=df['var'].mask(df['group'] == 'upper', -df['var'])
)
.sort_values(['group', 'sort_key']) # 先按 group(lower 在前),再按 sort_key(lower 升序,upper 因为负值而降序)
.groupby('region')
.cumcount() # 在每个 region 内从 0 开始连续编号
)
print(df.sort_values(['region', 'year', 'group']).reset_index(drop=True))✅ 输出结果与预期一致:
year region group var average loop 0 2010 1 lower 10 15.0 0 1 2010 1 upper 20 15.0 3 2 2011 1 lower 30 35.0 1 3 2011 1 upper 40 35.0 2 4 2010 2 lower 50 55.0 0 5 2010 2 upper 60 55.0 3 6 2011 2 lower 70 75.0 1 7 2011 2 upper 80 75.0 2
? 关键技巧说明:
- transform('mean') 确保 average 列与原始行对齐,无需聚合丢失结构;
- mask(..., -df['var']) 是核心 trick:将 upper 组的排序键设为负值,配合 sort_values(['group', 'sort_key']) 实现 lower(正数升序)→ upper(负数升序 ≡ 原值降序)的交错排列;
- groupby('region').cumcount() 保证每个区域独立编号,避免跨区域干扰。
⚠️ 注意事项:
- 该方法假设每个 (region, year) 恰好有且仅有 1 个 lower 和 1 个 upper 记录;若存在缺失或重复,需先用 drop_duplicates 或 pivot 校验结构;
- 若需扩展至更多边界(如 lower_5%, upper_95%),可将 group 映射为数值优先级,再统一排序;
- 最终 loop 序列可用于 matplotlib.patches.Polygon 或 geopandas.GeoDataFrame 构建闭合面——只需按 loop 升序提取坐标点即可。
掌握此模式后,你可灵活生成任意分组的带状多边形数据,为统计可视化打下坚实基础。










