Pandas如何基于另一列计数为指定列创建加权分位数分箱
带权重的分位数分箱高效实现
不需要展开重复行,直接基于累计权重计算分位点再切分即可,内存效率远高于重复行方案,权重值越大性能优势越明显。
核心逻辑分三步:
- 按待分箱的
conv_value字段升序排序 - 以
n_convs为权重计算累计权重占比,插值得到目标分位数对应的实际切分边界 - 用
pd.cut基于计算好的边界完成分箱
全量数据分箱示例
import pandas as pd import numpy as np # 测试数据 df = pd.DataFrame({ 'domain': ['etsy.com', 'etsy.com', 'etsy.com', 'allbirds.com', 'allbirds.com'], 'conv_value': [100.0, 50.0, 25.0, 80.0, 160.0], 'n_convs': [20, 100, 450, 1000, 5] }) q = 4 # 四分位,可按需修改分位数数量 # 排序后计算累计权重占比 df_sorted = df.sort_values('conv_value').reset_index(drop=True) total_conv = df_sorted['n_convs'].sum() cum_weight_ratio = np.concatenate([[0], df_sorted['n_convs'].cumsum() / total_conv]) sorted_values = np.concatenate([[df_sorted['conv_value'].iloc[0]], df_sorted['conv_value'].values]) # 插值得到分位切分边界,去重避免切分点重复报错 quantile_thresholds = np.linspace(0, 1, q+1) cut_bins = np.unique(np.interp(quantile_thresholds, cum_weight_ratio, sorted_values)) # 生成分箱结果 df['weighted_qcut_bin'] = pd.cut(df['conv_value'], bins=cut_bins, include_lowest=True)
这个方案得到的分箱结果和展开所有重复行后用pd.qcut计算的结果完全一致,但全程不会生成冗余的重复行,哪怕单条记录的n_convs达到百万、千万级别,内存占用也只和原始DataFrame的行数相关。
分组分箱扩展
如果需要按domain维度分别计算每个域名下的加权分箱,把逻辑封装成函数后配合groupby使用即可:
def calc_weighted_qcut(group, val_col, weight_col, q_num): g_sorted = group.sort_values(val_col).reset_index(drop=True) total_w = g_sorted[weight_col].sum() cum_w = np.concatenate([[0], g_sorted[weight_col].cumsum() / total_w]) s_vals = np.concatenate([[g_sorted[val_col].iloc[0]], g_sorted[val_col].values]) q_points = np.linspace(0, 1, q_num+1) bins = np.unique(np.interp(q_points, cum_w, s_vals)) return pd.cut(group[val_col], bins=bins, include_lowest=True) # 按域名分组生成分箱 df['bin_per_domain'] = df.groupby('domain', group_keys=False).apply( lambda x: calc_weighted_qcut(x, 'conv_value', 'n_convs', 4) )
注意:如果
conv_value存在大量重复值,可能出现分位点重合的情况,代码里的np.unique已经做了兼容处理,不会触发pd.cut的边界报错。
内容的提问来源于stack exchange,提问作者nurigashvili
相关产品推荐
相关产品推荐

