如何用Pandas筛选无月份间隔的用户交易数据?
问题分析与解决方案
原代码返回空结果主要有两个核心问题:
- 固定秒数判断月份间隔完全不可靠:不同月份的天数差异极大(28-31天),用固定的2629746秒(约30.44天)会频繁误判——比如1月31日到2月28日的间隔是28天,秒数远小于阈值,但实际是跨了完整月份;而3月1日到4月1日是31天,秒数会超过阈值,被误判为存在间隔。
- diff()的首个值为NaN:
diff()计算当前行与前一行的差值,第一行没有前置行,结果是NaN。all()会将NaN视为False,导致所有用户组都不满足条件,最终过滤结果为空。
正确实现方式
我们直接基于年月维度判断连续性,避开秒数计算误差和NaN问题,以下是两种高效的实现方案:
方案一:对比连续年月范围
import pandas as pd # 1. 提取交易日期的年月(YYYY-MM周期格式) dataframe['year_month'] = dataframe['transaction_date'].dt.to_period('M') # 2. 定义过滤函数:检查用户交易月份是否连续无缺失 def is_continuous_months(group): # 获取用户所有唯一的年月并排序 user_months = group['year_month'].sort_values().unique() # 生成从第一个到最后一个年月的完整连续序列 full_continuous_range = pd.period_range(start=user_months[0], end=user_months[-1], freq='M') # 若用户的月份数量与连续序列长度一致,说明无缺失 return len(user_months) == len(full_continuous_range) # 3. 按用户分组过滤 sorted_df = dataframe.sort_values(by=["username", "transaction_date"]) engaged_transactions = sorted_df.groupby("username").filter(is_continuous_months) # 清理临时列(可选) engaged_transactions = engaged_transactions.drop(columns='year_month')
方案二:检查相邻月份差值
这种方式更高效,无需生成完整连续范围,仅验证相邻月份的间隔是否为1个月:
import pandas as pd dataframe['year_month'] = dataframe['transaction_date'].dt.to_period('M') def is_continuous_months(group): user_months = group['year_month'].sort_values().unique() # 计算相邻年月的差值(转为整数,单位为月) month_gaps = (user_months[1:] - user_months[:-1]).astype(int) # 所有间隔必须为1才符合连续要求 return (month_gaps == 1).all() sorted_df = dataframe.sort_values(by=["username", "transaction_date"]) engaged_transactions = sorted_df.groupby("username").filter(is_continuous_months) engaged_transactions = engaged_transactions.drop(columns='year_month')
关键说明
- 用
dt.to_period('M')将日期转为年月周期,直接在月份维度处理,彻底规避了天数/秒数的计算误差。 - 两种方案都先对用户的年月去重排序,确保只考虑用户有交易的月份,而非重复的交易记录。
- 完全避开了
diff()带来的NaN问题,直接处理完整的月份序列。
内容的提问来源于stack exchange,提问作者Saylent
相关产品推荐
相关产品推荐

