如何按District列的值均匀重排行数不均的DataFrame?
按分组均匀重排DataFrame行(解决长度不匹配问题)
问题描述
现有一个随机生成的Pandas DataFrame,District列的各分组行数存在差异(如示例中alpha有10行、bravo有4行、charlie有9行),需要按District的值对行进行均匀穿插重排,但尝试的方法均出现长度不匹配错误。
输入示例:
Region District A B ... -------------------------- x alpha 1 4 ... x alpha 2 3 ... x alpha 3 4 ... x alpha 4 3 ... x alpha 5 4 ... x alpha 6 4 ... x alpha 7 3 ... x alpha 8 4 ... x alpha 9 3 ... x alpha 10 4 ... x bravo 11 3 ... x bravo 12 4 ... x bravo 13 3 ... x bravo 14 3 ... x charlie 15 4 ... x charlie 16 3 ... x charlie 17 4 ... x charlie 18 3 ... x charlie 19 4 ... x charlie 20 3 ... x charlie 21 4 ... x charlie 22 3 ... x charlie 23 4 ...
期望输出(均匀穿插各组行):
Region District A B ... -------------------------- x alpha 1 4 ... x alpha 2 3 ... x bravo 11 3 ... x charlie 15 4 ... x charlie 16 3 ... x alpha 3 4 ... x alpha 4 3 ... x bravo 12 4 ... x charlie 17 4 ... x charlie 18 3 ... x alpha 5 4 ... x alpha 6 4 ... x bravo 13 3 ... x charlie 19 4 ... x charlie 20 3 ... x alpha 7 3 ... x alpha 8 4 ... x bravo 14 3 ... x charlie 21 4 ... x charlie 22 3 ... x alpha 9 3 ... x alpha 10 4 ... x charlie 21 4 ... x charlie 22 3 ... x charlie 23 4 ...
核心问题分析
普通的zip或concat方法会因分组长度不一致报错:
zip会在最短分组结束后停止,丢弃长分组的剩余行;zip_longest会填充None,导致DataFrame列值不匹配。
解决方案
方法一:分组加批次号排序(推荐,简洁高效)
通过给每个分组内的行分配批次ID,再按批次ID排序,实现均匀穿插,完全避免长度不匹配问题。
代码实现
import pandas as pd # 构造示例DataFrame(可替换为你的数据) data = { 'Region': ['x']*23, 'District': ['alpha']*10 + ['bravo']*4 + ['charlie']*9, 'A': list(range(1,24)), 'B': [4,3,4,3,4,4,3,4,3,4] + [3,4,3,3] + [4,3,4,3,4,3,4,3,4] } df = pd.DataFrame(data) # 1. 定义每个分组的单次取数步长(可根据需求调整,示例匹配期望输出) step_dict = {'alpha': 2, 'bravo': 1, 'charlie': 2} # 2. 定义分组的排序优先级(控制同一批次内的分组顺序) district_order = ['alpha', 'bravo', 'charlie'] # 3. 给每行添加批次ID df['batch_id'] = df.groupby('District').apply( lambda g: g.index.to_series().apply(lambda x: (x - g.index[0]) // step_dict[g.name]) ).reset_index(level=0, drop=True) # 4. 给分组添加排序键(保证同一批次内的分组顺序) df['district_sort'] = df['District'].map(lambda x: district_order.index(x)) # 5. 排序并清理临时列 result = df.sort_values(by=['batch_id', 'district_sort']).drop(columns=['batch_id', 'district_sort']) # 查看结果 print(result)
通用适配(自动计算步长)
如果不需要手动指定步长,可根据各组长度比例自动计算:
# 计算各组长度 group_sizes = df.groupby('District').size() # 按最小分组长度计算步长,保证每个批次的循环次数匹配最小分组 step_dict = (group_sizes // group_sizes.min()).to_dict()
方法二:迭代器循环取数(直观灵活)
通过迭代器循环从每个分组中取指定数量的行,直到所有行取完,完全规避长度不匹配问题。
代码实现
import pandas as pd # 构造示例DataFrame data = { 'Region': ['x']*23, 'District': ['alpha']*10 + ['bravo']*4 + ['charlie']*9, 'A': list(range(1,24)), 'B': [4,3,4,3,4,4,3,4,3,4] + [3,4,3,3] + [4,3,4,3,4,3,4,3,4] } df = pd.DataFrame(data) # 定义分组顺序和单次取数步长 district_order = ['alpha', 'bravo', 'charlie'] step_dict = {'alpha': 2, 'bravo': 1, 'charlie': 2} # 按分组拆分DataFrame并转换为迭代器 groups = [df[df['District'] == d] for d in district_order] iters = [iter(g.itertuples(index=False, name=None)) for g in groups] step_list = [step_dict[d] for d in district_order] result_rows = [] # 循环取数直到所有迭代器为空 while True: has_rows = False for it, step in zip(iters, step_list): for _ in range(step): try: row = next(it) result_rows.append(row) has_rows = True except StopIteration: continue if not has_rows: break # 转换为DataFrame result = pd.DataFrame(result_rows, columns=df.columns) print(result)
效果验证
两种方法均能生成符合期望的均匀重排结果,且不会出现长度不匹配错误,适配任意分组长度差异的场景。
内容的提问来源于stack exchange,提问作者A.H
相关产品推荐
相关产品推荐

