嵌套循环处理两个大数据框匹配计数性能优化求助
优化方案
核心问题分析
嵌套iterrows()循环的时间复杂度为O(M*N)(M=330万,N=40万),总计算量达1.32e12次,完全无法在合理时间内完成。同时iterrows()返回行副本,原代码中修改row['PromoCount']不会实际更新DataFrame,这是一个隐藏bug。
优化方案1:Merge+分组统计(推荐,性能最优)
利用Pandas向量化合并与分组操作,将时间复杂度降至O(M+N)级别:
步骤1:统一日期格式为datetime
import pandas as pd # 转换日期列(若当前为字符串类型) SalesDF['SalesDays'] = pd.to_datetime(SalesDF['SalesDays']) SfPromos['Start date'] = pd.to_datetime(SfPromos['Start date']) SfPromos['End date'] = pd.to_datetime(SfPromos['End date'])
步骤2:合并表并筛选有效匹配
# 基于商品编码合并两个表,保留SalesDF所有行 merged = pd.merge( SalesDF[['article', 'SalesDays']], SfPromos[['Item Code', 'Start date', 'End date']], left_on='article', right_on='Item Code', how='left' ) # 筛选销售日期落在促销区间内的行 valid_matches = merged[ (merged['SalesDays'] >= merged['Start date']) & (merged['SalesDays'] <= merged['End date']) ]
步骤3:统计每行的促销次数
# 按SalesDF原始索引分组,统计匹配次数 promo_counts = valid_matches.groupby(valid_matches.index).size() # 赋值到PromoCount列,无匹配的行填充0 SalesDF['PromoCount'] = promo_counts.reindex(SalesDF.index, fill_value=0)
优化方案2:分组区间匹配(内存占用更低)
若Merge操作内存压力大,可按商品编码分组,针对每个商品的促销区间单独匹配:
步骤1:构建商品-促销区间字典
# 按Item Code分组,生成每个商品的促销区间索引 promo_dict = SfPromos.groupby('Item Code').apply( lambda x: pd.IntervalIndex.from_arrays(x['Start date'], x['End date'], closed='both') ).to_dict()
步骤2:计算每行促销次数
# 定义函数计算单一行的促销次数 def count_promos(row): intervals = promo_dict.get(row['article'], pd.IntervalIndex([])) return intervals.contains(row['SalesDays']).sum() # 用apply计算(比嵌套循环快100+倍,性能略逊于方案1) SalesDF['PromoCount'] = SalesDF.apply(count_promos, axis=1)
额外性能建议
- 确保
article和Item Code列是字符串/整数类型,避免混合类型拖慢匹配效率。 - 若内存不足,可按
article分组后分批处理,再合并结果。 - 禁用链式赋值,所有操作采用明确赋值方式。
内容的提问来源于stack exchange,提问作者Michael_vn22
相关产品推荐
相关产品推荐

