Pandas实现DataFrame按ticketid分组生成衍生聚合新列
按ticketid分组聚合的pandas实现方案
整体思路说明
你之前尝试以ticketid为索引定位分组的方向是成立的,但仅靠设置索引无法直接实现多值拆分、TopN频次提取的逻辑,更高效的方案是结合groupby分组、长格式转换、内置频次统计能力拆分实现,避免逐行遍历的低效率问题,最终可保证每个ticketid严格对应单行结果。
前置注意事项
- 修正示例代码里的列名大小写差异:原示例中列名
memberiD与需求描述的memberid不统一,提前对齐避免匹配报错 - 提前将date列转换为datetime类型:执行
df['date'] = pd.to_datetime(df['date']),保证取最早日期的min聚合逻辑正确,避免字符串排序出现结果偏差
分需求实现逻辑
1. 固定取值字段的聚合
针对memberid、agegrp这类同组取值完全一致的字段,直接使用内置first聚合提取固定值即可;date字段使用min聚合提取分组内最早日期,相关聚合配置可直接复用:
fixed_agg_config = { 'memberid': 'first', 'agegrp': 'first', 'date': 'min' } res_fixed = df.groupby('ticketid').agg(fixed_agg_config).reset_index()
2. c1/c2/c3字段的Top3高频值提取
先定义通用的TopK值提取函数,基于pandas内置value_counts实现频次排序,频次相同的tie场景下会按值首次出现顺序选取,完全满足“tie可任选”的要求:
def get_topk(s, k=3, drop_empty_str=False): # 移除空值 s = s.dropna() # 可选过滤空字符串 if drop_empty_str: s = s[s != ''] # 按频次降序取值,不足k位补空字符串固定长度 top_vals = s.value_counts().index.tolist()[:k] top_vals += [''] * (k - len(top_vals)) return top_vals
由于c1/c2/c3是三列独立的分类字段,需要将三列的所有取值统一纳入频次统计,这里用长格式转换的方式合并列,避免手动拼接单元格的低效率问题:
# 将c1/c2/c3三列转成长格式,关联对应的ticketid long_c = df.melt( id_vars=['ticketid'], value_vars=['c1', 'c2', 'c3'], value_name='c_content' ) # 按ticketid分组取Top3高频值,拼接为分隔符格式的结果 c_top3_res = long_c.groupby('ticketid')['c_content'].apply( lambda s: '|'.join(get_topk(s, k=3)) ).reset_index(name='c_top3_value')
注:如果需求是c1/c2/c3每个列单独提取自身的Top3值,而非三列合并统计,直接对每个列单独执行分组聚合即可,逻辑和d1列的处理完全一致。
3. d1字段的三列拆分
针对d1列提取Top3高频值、分别存入d1/d2/d3三个新列的需求,直接在分组时将提取到的Top3值拆为独立Series即可:
d_split_res = df.groupby('ticketid')['d1'].apply( lambda s: pd.Series( get_topk(s, k=3), index=['d1_top', 'd2_top', 'd3_top'] ) ).reset_index()
4. 结果合并
将上述三部分聚合结果按照ticketid主键合并,即可得到最终每个ticketid对应单行的聚合结果:
final_result = res_fixed.merge(c_top3_res, on='ticketid').merge(d_split_res, on='ticketid')
常见踩坑提醒
- 空字符串处理:示例中c3列存在空字符串,默认会被纳入频次统计,如果需要排除空值参与排序,调用
get_topk函数时传入drop_empty_str=True即可 - 空值干扰:统计频次前会自动移除nan值,避免nan被当作独立值参与TopN排序
- 排序一致性:pandas的
value_counts默认对同频次值按首次出现顺序排序,无需额外自定义tie规则
内容的提问来源于stack exchange,提问作者lvnwrth
相关产品推荐
相关产品推荐

