You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何加速大型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

关键优化点

  1. 砍掉无效merge:原代码里两次merge生成df2完全没必要,直接在分组里取最小日期就行,省了大量内存和计算。
  2. 分组内局部循环:只在每个分组内部遍历行,而非全表,效率直接从O(N²)降到O(N)。
  3. 预排序:确保每个分组内的日期是按顺序排列的,不然逻辑会出错(毕竟Excel也是按行的先后顺序处理的)。
  4. 减少loc操作:在分组里用列表暂存结果,最后一次性赋值给列,避免频繁修改DataFrame的巨大开销。

性能对比

90万行数据的话,原代码可能跑几个小时都完不成,优化后的代码几分钟就能搞定(具体看CPU性能,但至少快100倍以上)。

测试验证

用你给的测试数据跑优化后的代码,得到的Test和counter_2列结果和原代码完全一致,但速度快得不是一点半点。

内容的提问来源于stack exchange,提问作者mitzzzkrieg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 22:05:27