如何在Python DataFrame中按月统计跨类别重叠的唯一customer_id数量
按月统计多类别交易重叠客户数
我有一个存储金融交易信息的Python DataFrame,包含customer_id、date、product_type等列,每行是客户当月的交易记录,product_type为交易类别。目标是按月统计各类别组合中重叠的唯一customer_id数量,也就是每月内同时进行多类别交易的客户数。比如2022年6月“Category A”有10个唯一客户,“Category B”有6个,需要统计同时属于这两类的客户数。
输入数据示例
| customer_id | branch_id | product_type | date |
|---|---|---|---|
| CustomerId1 | Branch1 | Category A | 2022-06-30 22:47:35 |
| CustomerId2 | Branch2 | Category B | 2022-06-30 22:43:21 |
| CustomerId3 | Branch3 | Category A | 2022-06-30 22:43:04 |
| CustomerId4 | Branch1 | Category C | 2022-06-30 22:42:42 |
| CustomerId5 | Branch2 | Category B | 2022-06-30 22:31:03 |
| CustomerId6 | Branch3 | Category A | 2022-06-30 22:47:35 |
| CustomerId7 | Branch1 | Category D | 2022-06-30 22:43:21 |
| CustomerId8 | Branch2 | Category C | 2022-06-30 22:43:04 |
| CustomerId9 | Branch3 | Category E | 2022-06-30 22:42:42 |
| CustomerId10 | Branch1 | Category B | 2022-06-30 22:31:03 |
尝试的代码
import pandas as pd from itertools import combinations # DataFrame df with transaction information categories = ["Category A", "Category B", "Category C", "Category D", "Category E"] combined_tables = pd.DataFrame() for i in range(1, len(categories) + 1): for combo in combinations(categories, i): combo_name = ' & '.join(combo) filtered_df = df[df['product_type'].isin(combo)] # Count how many customer_ids overlap in the same category in each month grouped = filtered_df.groupby(['year', 'month', 'product_type'])['customer_id'].nunique().reset_index() grouped.rename(columns={'customer_id': combo_name}, inplace=True) if combined_tables.empty: combined_tables = grouped else: combined_tables = combined_tables.merge(grouped, on=['year', 'month', 'product_type'], how='left')
期望结果
希望得到包含year、month、各类别组合列的DataFrame,列值为对应组合中唯一客户数,示例如下:
| year | month | Category A | Category B | Category C | Category A & Category B | Category A & Category C | Category B & Category C | Category A & Category B & Category C |
|---|---|---|---|---|---|---|---|---|
| 2022 | 6 | 10 | 6 | 8 | 2 | 5 | 3 | 1 |
| 2022 | 7 | 7 | 9 | 4 | 3 | 2 | 1 | 0 |
| 2022 | 8 | 6 | 5 | 7 | 2 | 4 | 3 | 1 |
| 2022 | 9 | 9 | 4 | 8 | 1 | 3 | 2 | 0 |
| 2022 | 10 | 8 | 7 | 6 | 2 | 2 | 1 | 0 |
| 2022 | 11 | 7 | 8 | 5 | 1 | 2 | 1 | 0 |
| 2022 | 12 | 8 | 9 | 4 | 2 | 1 | 1 | 0 |
| 2023 | 1 | 9 | 6 | 7 | 1 | 2 | 1 | 0 |
| 2023 | 2 | 7 | 5 | 8 | 1 | 3 | 2 | 0 |
| 2023 | 3 | 8 | 4 | 9 | 2 | 4 | 3 | 1 |
| 2023 | 4 | 6 | 7 | 6 | 2 | 2 | 1 | 0 |
| 2023 | 5 | 5 | 6 | 7 | 1 | 3 | 2 | 0 |
| 2023 | 6 | 6 | 5 | 8 | 2 | 4 | 3 | 1 |
| 2023 | 7 | 7 | 4 | 9 | 1 | 2 | 1 | 0 |
需要覆盖所有类别组合。
解决方案
原代码的问题在于分组时错误包含了product_type,无法正确统计跨类别的重叠客户。正确思路是先聚合每个客户每月的交易类别集合,再判断集合是否包含目标组合,统计符合条件的客户数。
完整代码
import pandas as pd from itertools import combinations # 处理日期列,提取年、月 df['date'] = pd.to_datetime(df['date']) df['year'] = df['date'].dt.year df['month'] = df['date'].dt.month categories = ["Category A", "Category B", "Category C", "Category D", "Category E"] # 1. 聚合每个客户每月的交易类别集合 customer_month_cats = df.groupby(['year', 'month', 'customer_id'])['product_type'].agg(set).reset_index() customer_month_cats.rename(columns={'product_type': 'categories'}, inplace=True) # 2. 生成所有可能的类别组合(从1个到全类别) all_combos = [] for i in range(1, len(categories)+1): all_combos.extend(combinations(categories, i)) # 3. 统计每个年月下,符合各类别组合的客户数 result = customer_month_cats.groupby(['year', 'month']).apply( lambda x: pd.Series({ ' & '.join(combo): x['categories'].apply(lambda s: set(combo).issubset(s)).sum() for combo in all_combos }) ).reset_index() # 可选:按组合长度排序列(单类别在前,多类别在后) result = result[['year', 'month'] + sorted(result.columns[2:], key=lambda col: len(col.split(' & ')))] print(result)
代码说明
- 日期处理:将
date转为datetime类型,提取年、月字段用于分组。 - 客户-月类别集合:聚合每个客户当月的所有交易类别,用集合存储便于后续子集判断。
- 类别组合生成:遍历所有组合长度,生成所有可能的类别组合。
- 组合客户统计:对每个年月分组,判断客户的类别集合是否包含当前组合,统计符合条件的客户数量。
- 列排序:可选步骤,让单类别列在前,多类别列在后,结果更直观。
内容的提问来源于stack exchange,提问作者Mac_k17
相关产品推荐
相关产品推荐

