Pandas DataFrame按条件替换行值有没有比apply()更高效的实现?
问题原因分析
- 原
apply逐行方案的性能问题:每次循环都要全局过滤整个DataFrame获取指定id的告警日期,时间复杂度为O(n²),数据量达到十万级以上时耗时会指数级上升。 - 原有numpy写法的逻辑错误:你将id和日期的匹配拆成了两个独立的
isin判断,只要行id属于有过告警的id集合、且行日期属于所有告警日期的集合就会触发修改,没有绑定「同一个id的对应日期存在告警」的条件,因此会出现跨id误匹配的问题,比如示例中id=1111、日期=2020-12-06的行就被误改。
高效解决方案
用Pandas向量化操作实现,时间复杂度为O(n),20万行数据可在毫秒级完成计算,逻辑完全符合要求:
import pandas as pd import numpy as np # 第一步:提取所有存在告警的(id,日期)配对,去重避免重复判断 alert_pairs = df.loc[df['reset'] == 'Y', ['id', 'date']].drop_duplicates() # 第二步:构造布尔条件:当前行的(id,日期)配对属于告警配对集合 cond = df.set_index(['id', 'date']).index.isin(alert_pairs.set_index(['id', 'date']).index) # 第三步:条件匹配时替换tdelta为250 df['tdelta'] = np.where(cond, 250, df['tdelta'])
如果更习惯分组写法,也可以用如下代码,性能同样远高于逐行apply:
# 分组判断每个id+日期组是否存在告警 df['has_alert'] = df.groupby(['id', 'date'])['reset'].transform(lambda s: s.eq('Y').any()) df.loc[df['has_alert'], 'tdelta'] = 250 df.drop('has_alert', axis=1, inplace=True) # 不需要临时列可删除
效果验证
上述两种写法都可以完全匹配原apply逻辑的输出,示例中id=1111、日期=2020-12-06的行不会被误修改,符合业务要求。
内容的提问来源于stack exchange,提问作者Ze0ruso
相关产品推荐
相关产品推荐

