分组计算每行距离上次incident发生的天数(Pandas)
问题描述
现有如下DataFrame:
| customer_id | start_date | end_date | incident |
|---|---|---|---|
| 1 | 2022-01-01 | 2022-01-03 | False |
| 1 | 2022-01-02 | 2022-01-04 | True |
| 1 | 2022-01-04 | 2022-01-06 | False |
| 1 | 2022-01-05 | 2022-01-08 | False |
| 1 | 2022-01-05 | 2022-01-06 | False |
需要按customer_id分组,为每行计算距离上次incident(值为True)发生的天数:用当前行的start_date减去最后一行incident等于True的end_date,得到间隔天数。
期望输出:
| customer_id | start_date | end_date | incident | days_since_last_incident |
|---|---|---|---|---|
| 1 | 2022-01-01 | 2022-01-03 | False | NaN |
| 1 | 2022-01-02 | 2022-01-04 | True | NaN |
| 1 | 2022-01-04 | 2022-01-06 | False | 0 |
| 1 | 2022-01-05 | 2022-01-08 | False | 1 |
| 1 | 2022-01-05 | 2022-01-06 | False | 1 |
当前尝试用apply函数处理,但无前置incident的行出现越界错误,仅对存在前置incident的行有效,代码如下:
def days_since_last_incident(group): group["days_since_last_incidents"] = group.apply( lambda row: ( row["start_date"] - ( group[ (group["incident"] == True) & (group["end_date"] <= row["start_date"]) ]["end_date"].values ) ).days, axis=1, ) df.groupby("customer_id").apply(days_since_last_incident)
优雅实现方案
可以利用pandas的分组、填充和日期矢量化运算实现,避免逐行apply的低效和越界问题:
代码实现
import pandas as pd # 转换日期列为datetime类型(如果原始数据是字符串格式) df['start_date'] = pd.to_datetime(df['start_date']) df['end_date'] = pd.to_datetime(df['end_date']) # 分组提取最近一次incident的end_date并向前填充 df['last_incident_end'] = df.groupby('customer_id').apply( lambda g: g['end_date'].where(g['incident']).ffill() ).reset_index(level=0, drop=True) # 计算天数间隔 df['days_since_last_incident'] = (df['start_date'] - df['last_incident_end']).dt.days # 为incident=True的行和无前置incident的行设置NaN mask = df['incident'] | df['last_incident_end'].isna() df.loc[mask, 'days_since_last_incident'] = pd.NA # 删除辅助列(可选) df = df.drop('last_incident_end', axis=1) print(df)
代码说明
- 日期类型转换:确保日期列是
datetime格式,才能进行后续运算 - 提取并填充最近incident日期:
g['end_date'].where(g['incident']):仅保留incident为True的行的end_date,其余行设为NaNffill():向前填充NaN,让每一行自动继承最近一次incident的end_date
- 天数计算:直接用日期列做差后提取天数,全程矢量化操作,效率远高于逐行
apply - NaN值处理:通过掩码把incident本身为True的行、以及没有前置incident的行的结果设为NaN,完全匹配需求
输出结果
运行后将得到与期望完全一致的DataFrame。
内容的提问来源于stack exchange,提问作者Felix
相关产品推荐
相关产品推荐

