如何基于多条件匹配Pandas DataFrame行并优化现有低效代码
Pandas双DataFrame匹配效率优化方案
原场景说明
有两个DataFrame定义如下:
import pandas as pd df_1 = pd.DataFrame( {'num': [1,2,3], 'time': [100,200,300]}) df_2 = pd.DataFrame( {'num': [1,2,3], 'time': [101,104,200]})
匹配规则:
- df1的
num列值与df2的num列值不相等 - df1的
time值与df2的time值差值绝对值不超过10 - 每个df1的行最多取第一个匹配到的df2行,结果存入
df_3
原实现采用iterrows逐行遍历+逐行过滤df2的逻辑,数据量较大时性能很差。
优化实现(向量化操作)
核心思路是用全量交叉连接代替逐行迭代,利用pandas向量化运算提升效率,适合各规模的数据集:
# 先给两个表的列加后缀避免重名,同时保留原始索引用于分组去重 df1_tmp = df_1.add_suffix('_df1').reset_index(names='df1_idx') df2_tmp = df_2.add_suffix('_df2').reset_index(names='df2_idx') # 交叉连接两个表(pandas版本<1.2可替换为:df1_tmp.assign(tmp=1).merge(df2_tmp.assign(tmp=1), on='tmp')) cross_df = df1_tmp.merge(df2_tmp, how='cross') # 一次性过滤满足匹配规则的所有行 match_df = cross_df[ (cross_df['num_df1'] != cross_df['num_df2']) & (abs(cross_df['time_df1'] - cross_df['time_df2']) <= 10) ] # 按df1的原始索引分组,取第一个匹配的结果即可 df_3 = match_df.groupby('df1_idx', as_index=False).first()[ ['num_df1', 'num_df2', 'time_df1', 'time_df2'] ]
运行得到的df_3和原实现结果完全一致:
| num_df1 | num_df2 | time_df1 | time_df2 | |
|---|---|---|---|---|
| 0 | 1 | 2 | 100 | 104 |
| 1 | 2 | 3 | 200 | 200 |
性能说明
当df1和df2的数据量达到万级以上时,该方案的运行效率比原iterrows实现高至少2个数量级:
- 原实现时间复杂度为O(n*m),且每次循环都有Python层的迭代开销、DataFrame过滤开销和append的拷贝开销
- 优化方案的所有运算都在pandas的C语言层执行,仅保留一次过滤和分组操作,开销极低
如果数据集规模达到十万级以上,还可以进一步用numpy广播运算或numba编译加速,上述方案已经可以满足绝大多数场景的性能需求。
内容的提问来源于stack exchange,提问作者levi
相关产品推荐
相关产品推荐

