如何加速大型Pandas DataFrame中的for循环执行
问题描述
我手里有个90万行的Pandas DataFrame,要跑下面这段Python逻辑。小数据集上跑着没问题,但大数据集直接歇菜——处理1000行要90秒,超过1万行就直接卡住无限运行。我知道用apply会快很多,但不知道怎么把嵌套for循环转成apply写法。
原代码如下:
import pandas as pd df = pd.read_csv('data - Copy.csv') df[["Treat_Date"]] = df[["Treat_Date"]].apply(pd.to_datetime) df ## 为每个key生成最小日期 df1 = df.groupby(['key'])['Treat_Date'].min().reset_index() df1 = df.merge(df1) df2 = df.merge(df1, how='inner', on=['key']) ## 给每个key加计数器,标记第一条记录 df2['counter'] = df2.groupby(['key']).cumcount()+1 df2 ## 对应Excel里的公式:=IF(B3<=F2+90,F2,B3) ## 在Python里复现这个Excel逻辑 df1_grouped = df2.groupby('key') df1_grouped # 遍历每个key的分组 for group_name, df_group in df1_grouped: for ind, row in df2.iterrows(): if row['counter'] == 1 : df2.loc[ind,"Test"] = row['Treat_Date_y'] if row['counter'] != 1 : if row['Treat_Date_x'] <= df2.loc[ind-1,"Test"] + pd.Timedelta(days=90) : df2.loc[ind,"Test"] = df2.loc[ind-1,"Test"] else: df2.loc[ind,"Test"] = row['Treat_Date_x'] ## 给每个key和Test的组合加计数器 ## 值为1的是首次记录,2及以上属于同一时间窗口的重复记录 df3 = df2.copy() df3['counter_2'] = df3.groupby(['key','Test']).cumcount()+1 df3
测试数据:
key Treat_Date 6262452_Krish's Medical Center 5/19/2019 6262452_Krish's Medical Center 6/20/2019 6262452_Krish's Medical Center 7/29/2019 6262452_Krish's Medical Center 9/3/2019 6262452_Krish's Medical Center 10/13/2019 6262452_Krish's Medical Center 12/29/2020 6262452_Krish's Medical Center 2/3/2021 6262452_Krish's Medical Center 3/13/2021 6262452_Krish's Medical Center 4/18/2021 6262452_Krish's Medical Center 5/24/2021 6262452_Krish's Medical Center 7/5/2021 6262452_Krish's Medical Center 8/19/2021 6262452_Krish's Medical Center 9/22/2021 6262452_Krish's Medical Center 10/27/2021 6262452_Krish's Medical Center 12/8/2021 6262452_Krish's Medical Center 7/5/2022 6262452_Krish's Medical Center 7/30/2022 6262452_Mithuns Medical Center 5/19/2019 6262452_Mithuns Medical Center 6/20/2019 6262452_Mithuns Medical Center 7/29/2019 6262452_Mithuns Medical Center 9/3/2019 6262452_Mithuns Medical Center 10/13/2019 6262452_Mithuns Medical Center 12/29/2020 6262452_Mithuns Medical Center 2/3/2021 6262452_Mithuns Medical Center 3/13/2021 6262452_Mithuns Medical Center 4/18/2021 6262452_Mithuns Medical Center 5/24/2021 6262452_Mithuns Medical Center 7/5/2021 6262452_Mithuns Medical Center 8/19/2021 6262452_Mithuns Medical Center 9/22/2021 6262452_Mithuns Medical Center 10/27/2021 6262452_Mithuns Medical Center 12/8/2021 6262452_Mithuns Medical Center 7/5/2022 6262452_Mithuns Medical Center 7/30/2022
问题根源
原代码最大的坑是嵌套循环逻辑完全写错了:外层按key分组后,内层居然遍历整个df2而不是当前分组的行,等于每个key分组都要把90万行全跑一遍,直接把效率拉到谷底。再加上市面上最不推荐的df.loc循环修改数据,速度慢到离谱是必然的。
优化方案
我们可以直接对每个key分组应用自定义函数,只在分组内部处理行,避免全表遍历。另外原代码里两次merge的操作完全多余,直接在分组里取最小日期就行。
优化后的完整代码
import pandas as pd # 读数据+转日期格式 df = pd.read_csv('data - Copy.csv') df['Treat_Date'] = pd.to_datetime(df['Treat_Date']) # 先按key+日期排序,确保分组内的行是按时间顺序来的(这是逻辑正确的前提,Excel也是按行序处理) df = df.sort_values(['key', 'Treat_Date']).reset_index(drop=True) # 定义处理单个分组的函数 def process_single_group(group): # 初始化Test列,第一条记录用当前组的最小日期(和原逻辑一致) test_values = [group['Treat_Date'].min()] # 从第二条记录开始遍历 for date in group['Treat_Date'].iloc[1:]: last_test_date = test_values[-1] if date <= last_test_date + pd.Timedelta(days=90): test_values.append(last_test_date) else: test_values.append(date) # 把结果赋值给分组的Test列 group['Test'] = test_values # 生成counter_2列 group['counter_2'] = group.groupby('Test').cumcount() + 1 return group # 对每个key分组应用函数,group_keys=False避免保留分组键的索引 df3 = df.groupby('key', group_keys=False).apply(process_single_group) # 如果需要保留原代码里的counter列(标记组内的行号),加上这句 df3['counter'] = df3.groupby('key').cumcount() + 1
关键优化点
- 砍掉无效merge:原代码里两次merge生成
df2完全没必要,直接在分组里取最小日期就行,省了大量内存和计算。 - 分组内局部循环:只在每个分组内部遍历行,而非全表,效率直接从O(N²)降到O(N)。
- 预排序:确保每个分组内的日期是按顺序排列的,不然逻辑会出错(毕竟Excel也是按行的先后顺序处理的)。
- 减少
loc操作:在分组里用列表暂存结果,最后一次性赋值给列,避免频繁修改DataFrame的巨大开销。
性能对比
90万行数据的话,原代码可能跑几个小时都完不成,优化后的代码几分钟就能搞定(具体看CPU性能,但至少快100倍以上)。
测试验证
用你给的测试数据跑优化后的代码,得到的Test和counter_2列结果和原代码完全一致,但速度快得不是一点半点。
内容的提问来源于stack exchange,提问作者mitzzzkrieg
相关产品推荐
相关产品推荐

