如何用Python按指定行数与值总和规则拆分Pandas DataFrame?
数据集分组拆分的Python实现方案
核心需求回顾
- 4792行数据,总值33367
- 拆分8组:
- 前7组:每组≈639行,总和≈4448.93
- 第8组:≈319行,总和≈2224.5
方法一:高低值配对法(易实现、效率高)
这个思路和你提到的「高值搭配低值」逻辑一致,利用全局平均值和分组目标平均值高度接近的特点,通过偏差值配对快速凑出符合要求的组:
步骤
- 计算偏差值:给每条数据添加字段
deviation = 该行值 - 6.963(全局平均值,和分组目标平均值几乎一致) - 排序:将数据按
deviation从大到小排序 - 双指针分组:用左右指针分别从最大偏差和最小偏差取数,每次取数后更新当前组的行数和总和,直到接近目标阈值(比如行数638-640、总和4440-4458),完成一组后重置指针处理剩余数据。
Python代码示例
import pandas as pd # 假设数据集是df,包含数值列'value' df = pd.read_csv('your_dataset.csv') df['deviation'] = df['value'] - 6.963 # 按偏差从大到小排序 df_sorted = df.sort_values('deviation', ascending=False).reset_index(drop=True) groups = [] remaining_indices = set(range(len(df_sorted))) # 处理前7组 for _ in range(7): current_group = [] current_sum = 0.0 current_count = 0 left, right = 0, len(df_sorted) - 1 # 定位到剩余数据的首尾指针 while left not in remaining_indices: left += 1 while right not in remaining_indices: right -= 1 # 凑组:先取高偏差值,再补低偏差值平衡总和 while current_count < 639 and remaining_indices: # 优先加左指针的高偏差值 if left in remaining_indices and (current_sum + df_sorted.loc[left, 'value'] <= 4448.93 + 10 or current_count < 630): current_group.append(left) current_sum += df_sorted.loc[left, 'value'] current_count += 1 remaining_indices.remove(left) left += 1 while left not in remaining_indices and left <= right: left += 1 # 再补右指针的低偏差值 elif right in remaining_indices and current_count < 639: current_group.append(right) current_sum += df_sorted.loc[right, 'value'] current_count += 1 remaining_indices.remove(right) right -= 1 while right not in remaining_indices and right >= left: right -= 1 else: break groups.append(df_sorted.loc[current_group]) # 处理第8组(剩余数据自动符合行数和总和要求) groups.append(df_sorted.loc[list(remaining_indices)]) # 验证每组结果 for i, group in enumerate(groups): print(f"第{i+1}组:行数{len(group)},总和{group['value'].sum():.2f}")
方法二:启发式背包算法(追求更精准的总和)
如果高低值配对的结果精度不够,可以用启发式背包算法(精确背包在4k数据量下计算量过大,启发式足够满足需求):
核心思路
针对每个组,从剩余数据中选择指定数量的行,通过贪心替换调整总和,使其尽可能接近目标值:
- 随机选取初始组,计算与目标总和的偏差
- 迭代替换:总和偏高就替换组内高值为剩余数据的低值;总和偏低就替换组内低值为剩余数据的高值,直到偏差在可接受范围内。
简化代码示例
import pandas as pd def build_group(remaining_df, target_count, target_sum, tolerance=5): # 随机选初始组 group = remaining_df.sample(n=target_count) current_sum = group['value'].sum() while abs(current_sum - target_sum) > tolerance: diff = current_sum - target_sum if diff > 0: # 替换组内最高值为剩余数据的最低值 max_idx = group['value'].idxmax() min_remaining_idx = remaining_df.drop(group.index)['value'].idxmin() group = group.drop(max_idx).append(remaining_df.loc[min_remaining_idx]) else: # 替换组内最低值为剩余数据的最高值 min_idx = group['value'].idxmin() max_remaining_idx = remaining_df.drop(group.index)['value'].idxmax() group = group.drop(min_idx).append(remaining_df.loc[max_remaining_idx]) current_sum = group['value'].sum() return group # 初始化剩余数据 remaining_df = df.copy() groups = [] # 处理前7组 for _ in range(7): group = build_group(remaining_df, 639, 4448.93) groups.append(group) remaining_df = remaining_df.drop(group.index) # 处理第8组 groups.append(remaining_df) # 验证每组结果 for i, group in enumerate(groups): print(f"第{i+1}组:行数{len(group)},总和{group['value'].sum():.2f}")
注意事项
- 数据存在较多高值 outliers(中位数2、标准差13.644),高低值配对法能快速平衡总和,优先推荐
- 启发式背包的
tolerance参数可根据精度要求调整,比如设为1即允许总和偏差±1
内容的提问来源于stack exchange,提问作者Xemrer
相关产品推荐
相关产品推荐

