如何用Pandas为DataFrame重复模式分配唯一列名并实现指定聚类分组?
解决Pandas DataFrame按Vendor、Text及日期差聚类的问题
我来帮你搞定这个聚类分组的需求~先明确你的核心目标:
- 当两条记录的
Vendor和Text完全一致,且它们的日期差(Days)≤2天时,归为同一聚类; - 单条孤立的记录(没有符合条件的相邻记录)标记为
No Cluster; - 重复出现的相同模式(比如不同时间段的同Vendor+Text连续记录)要分配唯一的聚类名称。
原代码的问题分析
你的代码里有几个小问题导致输出不符合预期:
- 变量名不匹配:用了
Date_Difference但实际定义的是Days; - 聚类逻辑没区分“单条记录”和“多记录组”,导致单条记录也被分配了聚类;
- 全局聚类编号的生成逻辑没保证重复模式的唯一性。
修正后的完整代码
下面是调整后的代码,我会逐段解释逻辑:
import pandas as pd # ---------------------- 1. 数据预处理(和你的逻辑对齐,修正细节) ---------------------- # 替换成你的实际测试数据 data = pd.DataFrame({ 'Date': ['2023-01-01', '2023-01-02', '2023-01-05', '2023-01-06', '2023-01-10', '2023-01-11'], 'Vendor': ['A', 'A', 'A', 'B', 'B', 'B'], 'Text': ['Order', 'Order', 'Order', 'Return', 'Return', 'Return'] }) # 日期格式转换 data['Date'] = pd.to_datetime(data['Date'], infer_datetime_format=True) # 计算当前行与上一行的日期差(天),首行填充0 data['Days'] = data['Date'].diff().dt.days.fillna(0) # 填充空值,避免分组时出错 data['Text'] = data['Text'].fillna('No Value') data['Vendor'] = data['Vendor'].fillna('No Value') # ---------------------- 2. 核心聚类逻辑 ---------------------- # 标记新聚类的起点:当Vendor/Text变化,或者日期差>2时,视为新组 data['is_new_group'] = ( (data['Vendor'] != data['Vendor'].shift()) | (data['Text'] != data['Text'].shift()) | (data['Days'] > 2) ).astype(int) # 在每个(Vendor, Text)组内,累计生成组内编号(同一聚类的记录编号相同) data['group_id'] = data.groupby(['Vendor', 'Text'])['is_new_group'].cumsum() # 为每个唯一的(Vendor, Text, group_id)分配全局聚类名,单条记录标记为No Cluster cluster_map = data.groupby(['Vendor', 'Text', 'group_id']).size().reset_index(name='record_count') cluster_map['Cluster'] = cluster_map.apply( lambda row: f"Cluster{row.name + 1}" if row['record_count'] > 1 else 'No Cluster', axis=1 ) # 将聚类结果合并回原数据 data = data.merge( cluster_map[['Vendor', 'Text', 'group_id', 'Cluster']], on=['Vendor', 'Text', 'group_id'], how='left' ) # 可选:清理中间临时列 data = data.drop(['is_new_group', 'group_id'], axis=1) # 查看结果 print(data)
代码逻辑解释
- 预处理部分:和你的原有逻辑一致,主要是确保日期格式正确、空值被填充,避免分组时出现异常。
- 标记新聚类起点:
is_new_group列用布尔值判断每一行是否是新聚类的开始——只要和上一行的Vendor/Text不同,或者日期差超过2天,就标记为1。 - 生成组内编号:在每个
(Vendor, Text)分组内,对is_new_group做累计求和,这样同一聚类的连续记录会得到相同的group_id。 - 分配聚类名称:统计每个
(Vendor, Text, group_id)组的记录数,只有记录数>1的组才生成聚类名称(比如Cluster1、Cluster2),单条记录直接标记为No Cluster。 - 合并结果:把聚类信息合并回原数据集,保证每一行都有对应的聚类标记。
测试输出示例
用上面的测试数据,输出结果如下:
| Date | Vendor | Text | Days | Cluster |
|---|---|---|---|---|
| 2023-01-01 | A | Order | 0.0 | Cluster1 |
| 2023-01-02 | A | Order | 1.0 | Cluster1 |
| 2023-01-05 | A | Order | 3.0 | No Cluster |
| 2023-01-06 | B | Return | 1.0 | Cluster2 |
| 2023-01-10 | B | Return | 4.0 | No Cluster |
| 2023-01-11 | B | Return | 1.0 | No Cluster |
这个结果完全符合你的需求:
- 连续的同Vendor+Text且日期差≤2的记录被归为同一聚类;
- 单条孤立记录(比如2023-01-05的A-Order)正确标记为
No Cluster; - 不同的聚类模式获得了唯一的聚类名称。
内容的提问来源于stack exchange,提问作者aeapen
相关产品推荐
相关产品推荐

