解决pd.shift在缺失月份场景下无法匹配分类数据两个月前对应值的问题
嘿,我完全懂你的困扰!pd.shift()确实只关心行的位置顺序,不管实际日期差,一旦数据里有日期缺失(比如缺了某个月的记录),结果就完全不对了。咱们得换个思路——基于实际日期的匹配逻辑来实现需求,而不是单纯的行偏移。
先明确下两种常见的需求场景(因为你的预期结果和“两个月前”的描述有点小矛盾,我都列出来):
场景1:匹配「严格两个月前的日期」的col3值
如果你的需求是:对每条记录,找到同分类下日期正好是当前日期两个月前的col3值,不存在则显示NaN。
实现代码:
import pandas as pd import numpy as np # 加载你的数据(先转日期类型) df = pd.DataFrame({ 'Col1': ['A', 'A', 'B', 'B'], 'Col2': ['2020-01-08', '2020-01-11', '2020-01-06', '2020-01-08'], 'col3': [25, 26, 32, 45] }) df['Col2'] = pd.to_datetime(df['Col2']) # 定义分组处理函数 def match_exact_two_month_ago(group): # 构建「日期→col3值」的映射字典 date_to_value = group.set_index('Col2')['col3'].to_dict() # 计算每条记录的目标日期(当前日期减2个月) group['target_date'] = group['Col2'] - pd.DateOffset(months=2) # 匹配目标日期对应的col3值,无匹配则为NaN group['col4'] = group['target_date'].map(date_to_value) # 清理临时列 return group.drop('target_date', axis=1) # 按Col1分组处理 result = df.groupby('Col1').apply(match_exact_two_month_ago).reset_index(drop=True) print(result)
输出结果:
Col1 Col2 col3 col4 0 A 2020-01-08 25 NaN 1 A 2020-01-11 26 NaN 2 B 2020-01-06 32 NaN 3 B 2020-01-08 45 NaN
(因为所有记录的两个月前日期都不在原始数据里,所以col4全是NaN,符合逻辑)
场景2:匹配「当前日期前、且间隔≤2个月的最近记录」的col3值
如果你的需求是:对每条记录,找到同分类下日期早于当前日期、且与当前日期差不超过2个月的最近一条记录的col3值,没有符合条件的则显示NaN(这更符合你给出的预期结果逻辑)。
实现代码:
import pandas as pd import numpy as np # 加载你给出的预期示例数据(包含新增的A-2020-01-10行) df = pd.DataFrame({ 'Col1': ['A', 'A', 'A', 'B', 'B'], 'Col2': ['2020-01-08', '2020-01-10', '2020-01-11', '2020-01-06', '2020-01-08'], 'col3': [25, 56, 26, 32, 45] }) df['Col2'] = pd.to_datetime(df['Col2']) def match_recent_within_two_months(group): # 先按日期排序,确保顺序正确 group_sorted = group.sort_values('Col2').reset_index(drop=True) # 计算每条记录与前一条记录的日期差 group_sorted['date_diff'] = group_sorted['Col2'] - group_sorted['Col2'].shift(1) # 只有当日期差≤60天(约2个月)时,取前一条的col3值,否则为NaN group_sorted['col4'] = np.where( group_sorted['date_diff'] <= pd.Timedelta(days=60), group_sorted['col3'].shift(1), np.nan ) # 清理临时列 return group_sorted.drop('date_diff', axis=1) # 分组处理 result = df.groupby('Col1').apply(match_recent_within_two_months).reset_index(drop=True) print(result)
输出结果:
Col1 Col2 col3 col4 0 A 2020-01-08 25 NaN 1 A 2020-01-10 56 25.0 2 A 2020-01-11 26 56.0 3 B 2020-01-06 32 NaN 4 B 2020-01-08 45 32.0
这个结果和你给出的预期几乎一致,唯一的区别是A-2020-01-11的col4是56(因为它和前一条记录的日期差只有1天,符合≤2个月的条件),可能你的预期里这里是笔误?如果确实需要这条为NaN,可以再调整条件(比如只匹配正好两个月前或者其他规则)。
关键思路总结
- 放弃单纯依赖行位置的
shift(),转而基于日期计算+映射/条件判断来实现需求。 - 用
groupby()保证每个分类独立处理,避免跨分类匹配。 - 可以通过
pd.DateOffset()或pd.Timedelta()灵活定义时间间隔,适配不同的业务规则。
内容的提问来源于stack exchange,提问作者sayan_sen
相关产品推荐
相关产品推荐

