如何高效筛选DataFrame中与至少一个日期间隔≤6个月的行
高效处理百万级日期筛选:保留与至少一个日期间隔≤6个月的行
问题场景
现有包含Name、Address、Date三列的DataFrame:
Name Address Date faraz xyz 2022-01-01 Abdul abc 2022-06-06 Zara qrs 2021-02-25
需求:仅保留Date列中与至少一个其他日期间隔不超过6个月的行。示例中Zara的日期与另外两个间隔均超6个月,需删除,最终保留前两行。
原嵌套循环方案在百万级数据下效率极低,iterrows()+内层循环的时间复杂度为O(n*m),完全无法处理大数据量。以下是基于Pandas矢量化操作的最优解决方案:
方案1:基于merge_asof的精准匹配(适合含重复日期的大数据)
利用merge_asof高效找到每个日期的前后最近日期,通过矢量化计算判断间隔是否符合要求,时间复杂度为O(n log n)。
步骤1:预处理日期列
import pandas as pd # 转换Date列为datetime并排序 df['Date'] = pd.to_datetime(df['Date']) df_sorted = df.sort_values('Date').reset_index(drop=True)
步骤2:匹配前一个最近日期并计算间隔
# 准备前向匹配数据集 prev_dates = df_sorted[['Date']].rename(columns={'Date': 'prev_date'}) # 按日期反向匹配,找到每个日期的前一个最近日期(容忍183天≈6个月) df_prev = pd.merge_asof( df_sorted, prev_dates, left_on='Date', right_on='prev_date', direction='backward', tolerance=pd.Timedelta(days=183) ) # 计算精准月份差 df_prev['months_diff_prev'] = (df_prev['Date'].dt.year - df_prev['prev_date'].dt.year) * 12 + \ (df_prev['Date'].dt.month - df_prev['prev_date'].dt.month) # 无前置日期的行填充为无穷大 df_prev['months_diff_prev'] = df_prev['months_diff_prev'].fillna(float('inf'))
步骤3:匹配后一个最近日期并计算间隔
# 准备后向匹配数据集 next_dates = df_sorted[['Date']].rename(columns={'Date': 'next_date'}) # 按日期正向匹配,找到每个日期的后一个最近日期 df_next = pd.merge_asof( df_sorted, next_dates, left_on='Date', right_on='next_date', direction='forward', tolerance=pd.Timedelta(days=183) ) # 计算精准月份差 df_next['months_diff_next'] = (df_next['next_date'].dt.year - df_next['Date'].dt.year) * 12 + \ (df_next['next_date'].dt.month - df_next['Date'].dt.month) # 无后置日期的行填充为无穷大 df_next['months_diff_next'] = df_next['months_diff_next'].fillna(float('inf'))
步骤4:筛选符合条件的行
# 合并前后匹配结果 df_combined = df_prev.merge(df_next, on=['Name', 'Address', 'Date']) # 筛选:前一个或后一个日期间隔≤6个月 filtered_df = df_combined[(df_combined['months_diff_prev'] <= 6) | (df_combined['months_diff_next'] <= 6)] # 恢复原始顺序(可选) filtered_df = filtered_df.sort_index().reset_index(drop=True)
方案2:基于唯一日期的快速筛选(重复日期较多时更高效)
先处理唯一日期集合,再映射回原始数据,进一步减少计算量:
import pandas as pd df['Date'] = pd.to_datetime(df['Date']) # 获取排序后的唯一日期 unique_dates = pd.Series(df['Date'].sort_values().unique()) # 计算每个日期与前后日期的近似月份差(按平均每月30.44天计算) prev_diff = (unique_dates - unique_dates.shift(1)).dt.days / 30.44 next_diff = (unique_dates.shift(-1) - unique_dates).dt.days / 30.44 # 筛选有效日期:前后至少有一个间隔≤6个月 valid_dates = unique_dates[(prev_diff.fillna(float('inf')) <= 6) | (next_diff.fillna(float('inf')) <= 6)] # 过滤原始DataFrame filtered_df = df[df['Date'].isin(valid_dates)]
关键说明
merge_asof是Pandas专为有序键设计的高效合并工具,避免了逐行循环,适合百万级数据处理。- 月份差计算可选择精准月份差(年差*12+月差)或近似天数差(除以平均每月天数),按需选择。
- 重复日期较多时,优先使用方案2,能大幅降低计算开销。
内容的提问来源于stack exchange,提问作者Syed Faraz Khalid
相关产品推荐
相关产品推荐

