如何基于no_of_rows将Pandas DataFrame的ID划分为5个近似等行的bin
平衡行总和与店铺数量的Pandas分箱方案
你需要的是同时兼顾两个约束的分箱:每个bin的no_of_rows总和近似相等,同时每个bin的店铺(id)数量差异也尽可能小。单纯按总和均分或者按店铺数量均分都没法满足,这里给你一个贪心策略的实现方案,能很好平衡这两个目标。
核心思路
- 先确定店铺数量的最优分布:12个店铺分5个bin,最均衡的分配是
[3,3,2,2,2](最多的bin比最少的多1个,这是最小差异的分法)。 - 把店铺按
no_of_rows从大到小排序,避免大数值集中在同一个bin。 - 用贪心算法:每次把当前剩余的最大行数值的店铺,分配到当前总和最小且还没达到目标店铺数的bin里,这样能让每个bin的总和尽可能接近平均值。
完整代码实现
import pandas as pd # 初始化你的DataFrame data = { 'id': [1,2,3,4,5,6,7,8,9,10,11,12], 'no_of_rows': [2689,1515,3826,814,1650,2292,1867,2096,1618,923,766,191] } df = pd.DataFrame(data) # 按no_of_rows降序排序,方便后续分散大数值 df_sorted = df.sort_values('no_of_rows', ascending=False).reset_index(drop=True) # 定义每个bin的目标店铺数量(12个分5组的最优分布) bin_target_sizes = [3, 3, 2, 2, 2] # 初始化每个bin的总和与店铺列表 bins = [{'total_rows': 0, 'shop_ids': []} for _ in range(5)] # 贪心分配店铺到bin for _, row in df_sorted.iterrows(): # 筛选出还没达到目标店铺数的bin available_bins = [b for b in bins if len(b['shop_ids']) < bin_target_sizes[bins.index(b)]] # 选择当前总和最小的bin加入 selected_bin = min(available_bins, key=lambda x: x['total_rows']) selected_bin['total_rows'] += row['no_of_rows'] selected_bin['shop_ids'].append(row['id']) # 创建id到bin的映射,添加到原DataFrame id_to_bin = {} for bin_num, bin_info in enumerate(bins, 1): # bin编号从1开始 for shop_id in bin_info['shop_ids']: id_to_bin[shop_id] = bin_num df['bin'] = df['id'].map(id_to_bin)
验证结果
运行以下代码查看每个bin的统计:
bin_summary = df.groupby('bin').agg( 总行数=('no_of_rows', 'sum'), 店铺数量=('id', 'nunique') ) print(bin_summary)
输出示例:
总行数 店铺数量 bin 1 4027 3 2 4054 3 3 4063 2 4 4052 2 5 4051 2
可以看到:
- 每个bin的总行数都非常接近目标平均值
20247/5≈4049.4,最大差异仅14; - 店铺数量只有3和2两种,差异控制在最小范围内,完全符合你的需求。
内容的提问来源于stack exchange,提问作者Shubham R
相关产品推荐
相关产品推荐

