Pandas 基于条件列与分组计算顺序行日期间隔
原有代码报错原因
- apply调用逻辑错误:pandas的
apply方法要求传入可调用的函数对象,原代码中df.apply(func(df['offer_req_type'], df['or_date']))的写法是立即执行func函数,将函数的返回结果作为参数传给apply,pandas无法识别这个无名称的返回对象作为合法的可调用函数,因此抛出SpecificationError。 - 业务逻辑缺失:即便修正调用方式,原代码没有按公寓编号拆分计算范围,全局循环时会跨公寓累计报价日期,把不同公寓的报价、请求数据混算;同时函数中对报价行返回-1的处理,和样例中报价行间隔为0的规则不匹配,结果不符合预期。
适配业务需求的groupby实现方案
相比逐行循环的自定义函数,向量化的ffill(向前填充)方案效率更高,逻辑也完全匹配业务规则,步骤如下:
- 首先将文本格式的日期列转为pandas datetime类型:
import pandas as pd # 转换日期格式 df['or_date'] = pd.to_datetime(df['or_date']) - 构造临时列,仅保留所有报价行的日期,请求行的临时值为空:
df['recent_offer_date'] = df.loc[df['offer_req_type'] == 'offer', 'or_date'] - 按公寓编号分组,在每个公寓的已排序数据中,将最近一次报价的日期向前填充到后续所有行,直到遇到下一次报价才更新值:
df['recent_offer_date'] = df.groupby('apartment')['recent_offer_date'].ffill() - 计算日期差,生成目标列:
# 报价行自身的差值为0,首次报价前的请求行因无匹配报价日期自动得到空值,对应样例中的n/a df['days_since_offer'] = (df['or_date'] - df['recent_offer_date']).dt.days
过往方案不符合预期的原因
- 之前使用
df.groupby('offer_req_type').or_date.diff().dt.days的逻辑是按请求、报价类型分组计算相邻行差值,和业务要求的「按公寓追踪最近报价位置」完全不匹配,因此结果必然错误。 - 单纯用shift方法无法适配的核心原因是:shift是固定偏移固定行数,遇到连续多笔请求时,只能拿到上一行的日期,无法跳过中间的请求行、定位到真正的上一次报价日期;配合ffill的向前填充逻辑,就能跳过连续请求行,一直沿用最近的报价日期,直到遇到新的报价再更新。
自定义函数的适配写法
如果需要沿用原有自定义函数的逻辑,需要将其改造为适配groupby apply的形式,按每个公寓组单独计算,参考写法:
def calc_group_lapse(group): offer_dt = None lapse_list = [] for _, row in group.iterrows(): if row['offer_req_type'] == 'offer': offer_dt = row['or_date'] lapse_list.append(0) else: # 首次报价前的请求行标记为空,对应n/a lapse_list.append((row['or_date'] - offer_dt).days if offer_dt else None) group['days_since_offer'] = lapse_list return group # 按公寓分组应用计算,关闭group_keys避免多余层级 df = df.groupby('apartment', group_keys=False).apply(calc_group_lapse)
这个写法的计算结果和向量化ffill方案一致,但数据量大时循环效率会显著低于向量化操作。
内容的提问来源于stack exchange,提问作者KLANGE
相关产品推荐
相关产品推荐

