Pandas DataFrame:如何判断大tuple包含小tuple并统计课程对用户数
Pandas 课程对用户数统计实现方案
数据集选型结论
优先使用按用户聚合后的带tuple类型课程列的DataFrame实现需求。该方案计算量最小,逻辑完全匹配“大课程集合包含目标二元tuple即计入统计”的核心规则,13000行的聚合表不需要反复关联原始明细做重复扫描,性能和准确性都远优于基于原始明细逐次过滤的方案。
现有代码的失效原因
你当前编写的手动运行可正常工作的函数,在批量apply场景下失效的核心问题有两个:
- 逻辑硬编码了
len(x)==2的过滤条件:会把持有3门及以上课程的用户全部排除,完全不符合统计规则——比如持有(1,2,3)课程的用户,本该计入(1,2)、(1,3)、(2,3)三个课程对的统计,却会因为关联明细数为3被直接过滤,导致统计值严重偏小。手动测试时因为刚好只匹配了两门课的用户,没暴露这个bug,批量运行后所有漏算的问题叠加,就会出现结果偏差甚至运行报错。 - 性能瓶颈导致批量运行不稳定:每调用一次函数就要全表扫描一次原始明细、做三次分组聚合,4000个课程对就要重复扫描4000次明细数据,临时内存占用随调用次数累积,很容易触发索引对齐错误、内存溢出类的运行问题。
正确实现步骤
全程不需要逐课程对循环过滤,通过展开单用户的所有课程二元组合,一次聚合即可得到最终统计结果:
- 基础准备:
- 确认聚合用户表的结构为
uid列、course_id(tuple类型,存储单个用户的所有课程)列 - 提前处理课程对的顺序:所有二元tuple统一按课程id升序排列,避免出现(1,2)和(2,1)被当作两个不同pair重复统计的问题
- 确认聚合用户表的结构为
- 逐用户生成其持有的所有课程二元组合:
借助itertools.combinations自动展开单个用户课程tuple里的所有无重复二元组合,完全覆盖“大tuple包含小tuple”的判断逻辑——不管用户持有2门、3门还是更多课程,只要课程集合里存在目标pair对应的两个课程,就会自动生成对应的pair记录。 - 展开长表后分组计数,直接得到最终结果。
对应的可运行代码示例:
import itertools import pandas as pd # 输入:已聚合的用户课程表df_user_course,两列:uid, course_id(tuple类型) def generate_all_held_pairs(course_tuple): # 对单用户的课程集合去重、排序,生成所有二元组合 sorted_unique_courses = sorted(set(course_tuple)) return list(itertools.combinations(sorted_unique_courses, 2)) # 逐行生成每个用户能贡献的所有课程对 df_user_course['held_pairs'] = df_user_course['course_id'].apply(generate_all_held_pairs) # 把嵌套的pair列表展开为一行一个pair的长表 df_pair_long = df_user_course.explode('held_pairs') # 按pair分组,统计每个pair对应的去重用户数 df_result = df_pair_long.groupby('held_pairs', as_index=False)['uid'].nunique() # 重命名列匹配要求的输出结构 df_result.columns = ['pair', 'users']
如果你坚持使用未聚合的原始明细DataFrame实现,也需要先把明细按uid聚合为课程集合,再走上面的展开流程,不要逐pair反复扫描明细做过滤——明细到聚合的转换只需要执行一次,后续所有统计都基于聚合后的集合做匹配,才能保证性能和准确性。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

