如何在Pandas DataFrame中按ID获取指定时间范围的滞后/前置值
解决方案:Pandas中按日期约束获取分组的滞后/前置测量值
可以实现,核心是利用日期范围筛选或merge_asof方法,替代无日期约束的shift()。针对你的需求,具体步骤如下:
1. 预处理日期列
首先确保所有日期列是datetime类型,避免格式错误:
import pandas as pd # 假设你的数据集为df,包含字段:user_id, event_id, event_date, measure_date, measure_value df['event_date'] = pd.to_datetime(df['event_date']) df['measure_date'] = pd.to_datetime(df['measure_date'])
2. 方法一:用merge_asof高效匹配(推荐大数据量)
merge_asof支持按分组(user_id)和日期范围匹配最近的记录,完美解决shift()无日期约束的问题:
(1)获取事件前2个月内的最近测量值
# 拆分事件和测量数据(过滤空值干扰) events = df.dropna(subset=['event_id']).sort_values(['user_id', 'event_date']) measures = df.dropna(subset=['measure_value']).sort_values(['user_id', 'measure_date']) # 匹配事件前2个月内的最近测量 prev_measures = pd.merge_asof( events, measures, by='user_id', # 按用户分组匹配 left_on='event_date', right_on='measure_date', direction='backward', # 取小于等于事件日期的最近记录 tolerance=pd.DateOffset(months=2) # 限制在事件日期前2个月内 ) prev_measures = prev_measures.rename(columns={'measure_value': 'prev_measure'})
(2)获取事件后6个月内的最近测量值
next_measures = pd.merge_asof( events, measures, by='user_id', left_on='event_date', right_on='measure_date', direction='forward', # 取大于等于事件日期的最近记录 tolerance=pd.DateOffset(months=6) # 限制在事件日期后6个月内 ) next_measures = next_measures.rename(columns={'measure_value': 'next_measure'})
(3)合并结果
final_df = pd.merge( prev_measures[['user_id', 'event_id', 'event_date', 'prev_measure']], next_measures[['user_id', 'event_id', 'event_date', 'next_measure']], on=['user_id', 'event_id', 'event_date'], how='left' )
3. 方法二:分组后逐行筛选(直观,适合小数据量)
如果数据量不大,也可以通过分组后逐行筛选日期范围,取最近的测量值:
(1)定义获取前后测量值的函数
def get_prev_measure(row): # 筛选当前用户、事件日期前2个月内的测量记录 user_measures = df[ (df['user_id'] == row['user_id']) & (df['measure_date'] <= row['event_date']) & (df['measure_date'] >= row['event_date'] - pd.DateOffset(months=2)) ] if not user_measures.empty: # 取日期最近的(即最大的measure_date对应的测量值) return user_measures.loc[user_measures['measure_date'].idxmax(), 'measure_value'] return None def get_next_measure(row): # 筛选当前用户、事件日期后6个月内的测量记录 user_measures = df[ (df['user_id'] == row['user_id']) & (df['measure_date'] >= row['event_date']) & (df['measure_date'] <= row['event_date'] + pd.DateOffset(months=6)) ] if not user_measures.empty: # 取日期最近的(即最小的measure_date对应的测量值) return user_measures.loc[user_measures['measure_date'].idxmin(), 'measure_value'] return None
(2)应用函数到每个事件
df['prev_measure'] = df.apply(get_prev_measure, axis=1) df['next_measure'] = df.apply(get_next_measure, axis=1) # 保留需要的字段 final_df = df.dropna(subset=['event_id'])[['user_id', 'event_id', 'event_date', 'prev_measure', 'next_measure']]
关键说明
merge_asof要求左右数据集必须按by和on的字段排序,效率远高于逐行apply,适合十万级以上数据。- 两种方法都会自动处理“无符合条件测量值”的情况,返回
NaN,可根据需求填充默认值。
内容的提问来源于stack exchange,提问作者E. Camus
相关产品推荐
相关产品推荐

