如何加速DataFrame分析?20万行iterrows循环耗时过久求优化
高效优化20万行DataFrame时间区间求和方案
哇,20万行用iterrows()逐行处理确实会慢到离谱——我之前也踩过这个坑!问题的核心是你当前的写法是逐行遍历+全表筛选,时间复杂度达到了O(n²),20万行的话相当于要执行400亿次左右的操作,耗时几小时完全在意料之中。下面给你几个从易到难的高效优化方案,速度能提升几个数量级:
方案一:分组后组内矢量化处理(最推荐)
先按GROUP_ID分组,在每个组内完成时间区间的筛选和求和,这样可以把筛选范围从全表缩小到单个分组,再结合pandas的矢量化操作,效率会大幅提升:
import pandas as pd def calculate_group_gaps(group): # 先对组内数据按BEG_DATE排序,后续区间匹配更高效 group = group.sort_values('BEG_DATE', ignore_index=True) # 为每行创建时间区间:[THREE_YEAR_AGO, BEG_DATE] intervals = pd.IntervalIndex.from_arrays( group['THREE_YEAR_AGO'], group['BEG_DATE'], closed='both' # 包含两端日期 ) # 用numpy广播快速匹配所有END_DATE是否在对应区间内 end_dates = group['END_DATE'].values[:, None] # 生成布尔矩阵:行是原行,列是组内其他行,标记是否符合条件 match_mask = intervals.contains(end_dates) # 矩阵乘法快速计算每行对应的GAP求和 group['THREE_YR_GAPS'] = group['GAP'].values @ match_mask return group # 应用到每个分组,禁用group_keys避免额外索引 df = df.groupby('GROUP_ID', group_keys=False).apply(calculate_group_gaps) # 提取结果列表 three_yr_gaps = df['THREE_YR_GAPS'].tolist()
这个方案的优势是完全利用了pandas和numpy的矢量化优化,避免了Python层面的循环,单个分组内的操作都是C语言级别的速度。
方案二:自连接+分组聚合
如果对区间索引不太熟悉,也可以用自连接的方式实现:先把DataFrame按GROUP_ID自连接,筛选符合时间条件的行,再按原行索引聚合求和:
# 按GROUP_ID自连接,区分原表和连接表的列 merged = df.merge(df, on='GROUP_ID', suffixes=('', '_other')) # 筛选时间条件:连接表的BEG_DATE >= 原表的THREE_YEAR_AGO,且连接表的END_DATE <= 原表的BEG_DATE valid_mask = (merged['BEG_DATE_other'] >= merged['THREE_YEAR_AGO']) & (merged['END_DATE_other'] <= merged['BEG_DATE']) # 按原行的索引分组,对符合条件的GAP求和 summed_gaps = merged[valid_mask].groupby(merged.index)['GAP_other'].sum() # 把结果映射回原DataFrame,没有匹配项的填充0 df['THREE_YR_GAPS'] = summed_gaps.reindex(df.index, fill_value=0) three_yr_gaps = df['THREE_YR_GAPS'].tolist()
这个方案逻辑更直观,但自连接会产生较多中间数据,如果你的分组数量很少、每个分组数据量很大,内存占用可能会比较高,适合分组较多的场景。
额外优化建议
- 确保日期列是datetime类型:如果你的
BEG_DATE、END_DATE、THREE_YEAR_AGO是字符串类型,先转成datetime:df['BEG_DATE'] = pd.to_datetime(df['BEG_DATE']) df['END_DATE'] = pd.to_datetime(df['END_DATE']) df['THREE_YEAR_AGO'] = pd.to_datetime(df['THREE_YEAR_AGO']) - 优化数据类型:把
GROUP_ID设为category类型,减少内存占用和分组时间:df['GROUP_ID'] = df['GROUP_ID'].astype('category') - 提前排序:对每个分组按日期排序后,后续的区间匹配或筛选会更高效,方案一中已经包含了这一步。
内容的提问来源于stack exchange,提问作者MetaStack
相关产品推荐
相关产品推荐

