如何用Pandas将观测数不足阈值的相邻地理区域合并聚合?
问题描述
需要按编号标识的小型地理区域分组统计观测数据,相邻区域编号相近(如0001与0002、0003相邻)。对于观测数极少的区域,要和相邻区域合并,直到合并后的区域观测数达到指定阈值(如5)。同时需要对数据框中其他列(如收入中位数)进行聚合处理。
示例数据
import pandas as pd d = {'region': ['0001', '0001','0002','0002','0002','0002','0003','0003','0003','0003','0003','0003','0003','0003'], 'observation': ['A1', 'B45', 'B83578','L87846','I4536','J8659','H8756','G0853','P89058','A98','B89056980','U9690','Q9875','N896']} df = pd.DataFrame(data=d) # 现有分组统计代码 df_grouped = df.groupby('region')['observation'].count() print(df_grouped)
现有输出
region 0001 2 0002 4 0003 8 Name: observation, dtype: int64
期望输出
region [0001,0002] 6 0003 8 Name: observation, dtype: int64
解决方案
以下是实现需求的代码,包含区域合并逻辑和多列聚合处理:
import pandas as pd # 1. 准备包含额外列的示例数据 d = { 'region': ['0001', '0001','0002','0002','0002','0002','0003','0003','0003','0003','0003','0003','0003','0003'], 'observation': ['A1', 'B45', 'B83578','L87846','I4536','J8659','H8756','G0853','P89058','A98','B89056980','U9690','Q9875','N896'], 'income': [3000, 3500, 4000, 4200, 3800, 4500, 5000, 5200, 4800, 5500, 4900, 5300, 5100, 4700] } df = pd.DataFrame(data=d) # 2. 计算各区域初始统计量,并按区域编号排序(确保相邻区域顺序正确) region_stats = df.groupby('region').agg( obs_count=('observation', 'count') ).reset_index() # 将区域编号转为整数,避免字符串排序的异常(比如'0010'和'002'的排序问题) region_stats['region_num'] = region_stats['region'].astype(int) region_stats = region_stats.sort_values('region_num').reset_index(drop=True) # 3. 合并相邻区域直到满足阈值 threshold = 5 merged_groups = [] current_group = {'regions': [], 'total_obs': 0} for idx, row in region_stats.iterrows(): current_group['regions'].append(row['region']) current_group['total_obs'] += row['obs_count'] # 当当前组观测数达标,或是最后一个区域时,完成该组合并 if current_group['total_obs'] >= threshold or idx == len(region_stats) - 1: # 生成组名:单个区域直接用编号,多个区域用列表格式 group_name = current_group['regions'][0] if len(current_group['regions']) == 1 else str(current_group['regions']) # 提取当前组的所有原始数据,计算多列聚合值(这里以观测数、收入中位数为例) group_data = df[df['region'].isin(current_group['regions'])] merged_groups.append({ 'region': group_name, 'obs_count': group_data['observation'].count(), 'median_income': group_data['income'].median() }) # 重置当前组,准备下一轮合并 current_group = {'regions': [], 'total_obs': 0} # 4. 转换为DataFrame并输出 result = pd.DataFrame(merged_groups).set_index('region') print(result)
代码说明
- 排序处理:将区域编号转为整数排序,避免字符串排序导致的相邻区域顺序错误(比如'0010'会排在'002'前面的问题)。
- 合并逻辑:从第一个区域开始逐步合并相邻区域,直到观测数达到阈值,确保只合并相邻区域。
- 多列聚合:合并后通过提取原始数据重新计算聚合值(如中位数),比直接加权更准确,因为中位数这类统计量不能通过简单加权得到正确结果。
内容的提问来源于stack exchange,提问作者Khoin
相关产品推荐
相关产品推荐

