按application_id分组跨行比较时间戳生成user_rejects布尔列(需高效)
高效实现分组内时间戳匹配生成布尔序列的方案
需求回顾
- 按
application_id字段分组 - 每组内,标记当前行的
rejected_time是否等于组内其他任意行的selected_time,生成user_rejects布尔序列(用1/0表示) - 数据集为百万级行,每组对应15-25行,需保证处理效率
示例数据
| application_id | id | creation_timestamp | selected_time | rejected_time |
|---|---|---|---|---|
| 69c0 | 7 | 2023-11-20 05:32:26.691008 | 2023-11-20 05:32:26.691008 | 2023-11-21 08:30:20.881008 |
| 69c0 | 15 | 2023-11-20 05:32:26.691008 | NaT | 2023-12-20 05:32:26.691008 |
| 69c0 | 14 | 2023-11-20 05:32:26.691008 | NaT | 2023-12-04 05:32:26.691008 |
| 69c0 | 9 | 2023-11-20 05:32:26.691008 | NaT | 2023-12-20 05:32:26.691010 |
| 69c0 | 18 | 2023-11-20 05:32:26.691008 | NaT | 2023-12-20 05:32:26.691011 |
| 69c0 | 6 | 2023-11-20 05:32:26.691008 | 2023-11-21 08:30:20.881008 | NaT |
| 69c0 | 19 | 2023-11-20 05:32:26.691008 | NaT | 2023-12-11 05:32:26.691008 |
| db26 | 11 | 2023-08-01 10:40:48.473828 | 2023-08-01 10:40:48.473828 | |
| db26 | 12 | 2023-08-01 10:40:48.473828 | 2023-08-01 10:40:48.473828 |
预期输出
| application_id | id | creation_timestamp | selected_time | rejected_time | user_rejects |
|---|---|---|---|---|---|
| 69c0 | 7 | 2023-11-20 05:32:26.691008 | 2023-11-20 05:32:26.691008 | 2023-11-21 08:30:20.881008 | 1 |
| 69c0 | 15 | 2023-11-20 05:32:26.691008 | NaT | 2023-12-20 05:32:26.691008 | 0 |
| 69c0 | 14 | 2023-11-20 05:32:26.691008 | NaT | 2023-12-04 05:32:26.691008 | 0 |
| 69c0 | 9 | 2023-11-20 05:32:26.691008 | NaT | 2023-12-20 05:32:26.691010 | 0 |
| 69c0 | 18 | 2023-11-20 05:32:26.691008 | NaT | 2023-12-20 05:32:26.691011 | 0 |
| 69c0 | 6 | 2023-11-20 05:32:26.691008 | 2023-11-21 08:30:20.881008 | NaT | 0 |
| 69c0 | 19 | 2023-11-20 05:32:26.691008 | NaT | 2023-12-11 05:32:26.691008 | 0 |
| db26 | 11 | 2023-08-01 10:40:48.473828 | 2023-08-01 10:40:48.473828 | 0 | |
| db26 | 12 | 2023-08-01 10:40:48.473828 | 2023-08-01 10:40:48.473828 | 0 |
实现方案(基于Pandas)
1. 预处理时间列
统一处理空字符串和缺失值,转换为标准的NaT(时间类型缺失值):
import pandas as pd # 假设数据已加载到df中 # df = pd.read_csv('your_data.csv') # 转换时间列,自动处理空字符串为NaT df['selected_time'] = pd.to_datetime(df['selected_time'], errors='coerce') df['rejected_time'] = pd.to_datetime(df['rejected_time'], errors='coerce')
2. 分组处理函数
针对每个分组,先统计selected_time的出现频次,再快速判断每行的rejected_time是否满足条件:
def mark_user_rejects(group): # 统计组内非空selected_time的出现次数 selected_counts = group['selected_time'].dropna().value_counts() def check_match(row): rt = row['rejected_time'] # 若rejected_time为空,直接返回0 if pd.isna(rt): return 0 # 若rejected_time不在selected_time的集合中,返回0 if rt not in selected_counts: return 0 # 特殊情况:当前行的selected_time等于rejected_time,需确保组内至少有另一个相同的selected_time if row['selected_time'] == rt: return 1 if selected_counts[rt] > 1 else 0 # 其他情况,只要存在匹配就返回1 return 1 # 应用判断逻辑到每行 group['user_rejects'] = group.apply(check_match, axis=1) return group
3. 执行分组处理
# 按application_id分组处理,重置索引 df = df.groupby('application_id').apply(mark_user_rejects).reset_index(drop=True)
效率说明
- 每组仅15-25行,分组内的统计和判断操作复杂度极低(近似O(n))
- 避免了逐行交叉比较的O(n²)复杂度,适合百万级数据集
- Pandas的分组操作基于C实现的底层逻辑,进一步提升了处理速度
内容的提问来源于stack exchange,提问作者Alex Günsberg
相关产品推荐
相关产品推荐

