
本文介绍如何在 pandas 中对时间-区域数据分组计算变量均值,并按特定逻辑(lower 升序、upper 降序)排序后分配环状索引(loop),以支持后续多边形(如地理围栏或面积图)的顶点顺序构建。
在可视化或空间分析中,常需将成对的上下边界(如置信区间、预测上下限、地理高程带等)构造成闭合多边形。为此,不仅需要按组聚合统计量(如均值),还需为每组内的观测点分配一个首尾相连的顶点序号(loop ID),确保绘图时能按 0 → 1 → 2 → 3 → 0 的顺序闭合路径。
给定原始数据:
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)✅ 步骤一:计算每组(year + region)的平均值
使用 groupby(...).transform('mean') 可高效广播均值至每行,无需聚合再合并:
df['average'] = df.groupby(['year', 'region'])['var'].transform('mean')该列结果为:同一 year-region 组内所有 var 值的算术平均(如 2010-1 组:(10+20)/2 = 15)。
✅ 步骤二:生成环状 loop ID(关键逻辑)
目标是让每个 region 内的四行(2 年 × 2 组)按如下顺序编号:
[2010-lower, 2011-lower, 2011-upper, 2010-upper] → [0, 1, 2, 3]
实现策略:
- 对 lower 组保留 var 原值(升序排列即按年递增);
- 对 upper 组取 -var(因 var 本身随年份增大,取负后可使 2011-upper 排在 2010-upper 前,实现“upper 降序”);
- 按 group(lower 在前)、再按调整后的 var 排序;
- 对每个 region 内排序后的序列,用 cumcount() 从 0 开始编号。
完整代码如下:
df['loop'] = (
df.assign(var=df['var'].mask(df['group'].eq('upper'), -df['var']))
.sort_values(by=['group', 'var'])
.groupby('region').cumcount()
)? 关键说明:mask(..., -df['var']) 将 upper 行的 var 替换为负值,使排序时 upper 组内数值越大(如 80→-80),排序越靠前(因 -80✅ 最终结果验证
执行后,df 包含以下列:
year region group var average loop 2010 1 lower 10 15.0 0 2010 1 upper 20 15.0 3 2011 1 lower 30 35.0 1 2011 1 upper 40 35.0 2 2010 2 lower 50 55.0 0 2010 2 upper 60 55.0 3 2011 2 lower 70 75.0 1 2011 2 upper 80 75.0 2 可见:每个 region 内 loop 均为 [0, 3, 1, 2],对应多边形顶点顺序 (2010-lower, 2010-upper, 2011-lower, 2011-upper) 的逆时针环状连接(实际绘图时按 loop 升序取点即可闭合)。
⚠️ 注意事项
- 此方法假设每 region 内恰好有 2 个年份 × 2 个 group(共 4 行)。若数据不规则(如缺失某年或某组),需先补全(pd.MultiIndex.from_product + reindex)再应用。
- loop 是基于 region 分组独立编号的,跨 region 不连续,符合多边形独立绘制需求。
- 若需适配更多年份(如 3 年),逻辑可扩展为:lower 按年升序,upper 按年降序,再拼接两段索引(np.concatenate([np.arange(n), np.arange(n)[::-1][1:]]))。
该方案兼顾简洁性与可扩展性,是构建动态区间多边形(如 matplotlib.fill_between 或 geopandas.Polygon)的理想预处理步骤。










