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

pandas从0.25.1升级到2.1.4后,分组按月前向填充速度骤降

Pandas升级后数据填充性能暴跌的优化方案

背景与问题

我有总计约1000万行的月度.csv格式数据。在重新安装Anaconda并将pandas版本从0.25.1升级到2.1.4后,生成目标DataFrame的运行时间从约20分钟增至8小时。虽不确定是否与升级直接相关,但问题在升级后出现。

核心需求:若用户某月份未登录(无对应记录),需为其添加该行,并填充最近有登录月份的login_counter和last_login_timestamp值。

示例数据

Jan-26

customer;user_id;user_name;status;user_type;last_login_timestamp;login_counter;month
1111;1;A;1;Normal;11.01.2026 11:38;100;202601
1111;2;B;1;Normal;04.01.2026 11:11;50;202601
2222;4;P;1;Normal;04.01.2026 21:16;40;202601
2222;5;Q;1;Admin;12.01.2026 04:54;55;202601
2222;6;R;1;Normal;23.01.2026 21:06;20;202601
3333;9;X;2;Normal;04.01.2026 06:56;70;202601

Feb-26

customer;user_id;user_name;status;user_type;last_login_timestamp;login_counter;month
1111;1;A;1;Normal;21.02.2026 06:38;120;202602
2222;4;P;1;Normal;13.02.2026 18:11;50;202602
2222;5;Q;1;Admin;19.02.2026 14:51;60;202602
2222;6;R;1;Normal;11.02.2026 16:41;25;202602

Mar-26

customer;user_id;user_name;status;user_type;last_login_timestamp;login_counter;month
1111;1;A;1;Normal;05.03.2026 03:30;150;202603
1111;2;B;1;Normal;04.03.2026 21:51;60;202603
2222;4;P;1;Normal;12.03.2026 15:16;80;202603
2222;5;Q;1;Admin;24.03.2026 04:54;90;202603
2222;6;R;1;Normal;03.03.2026 21:06;45;202603
3333;9;X;2;Normal;21.03.2026 13:03;95;202603

每行对应某客户下的特定用户,例如客户1111有用户1和2;若用户某月份未登录则无对应记录,如客户3333的用户9在2026年2月无记录。

原处理代码

1. 合并所有数据文件

import pandas as pd
import os

df_all = pd.DataFrame()

for i in ['Data_202601.csv', 'Data_202602.csv', 'Data_202603.csv']:
    df = pd.read_csv(os.path.join(path, i), sep=';')
    df['last_login_timestamp'] = pd.to_datetime(
        df['last_login_timestamp'],
        format='%d.%m.%Y %H:%M')
    df['month'] = pd.to_datetime(df['month'], format='%Y%m')
    df_all = df_all._append(df)
df_all = df_all.sort_values(
    ['customer', 'user_id', 'month'],
    ascending=(True, True, True)
).reset_index(drop=True)

2. 缺失月份填充(性能瓶颈代码)

df_all = (df_all.set_index('month')
                .groupby(['customer', 'user_id'])
                .apply(lambda x: x.asfreq('MS', method='ffill'))
                .drop(['customer', 'user_id'], axis=1)).reset_index()

这段代码在Pandas升级后运行时间从20分钟暴涨至8小时,核心原因是groupby.apply的逐组处理在新版本中效率大幅降低,尤其是处理百万级数据时。

优化方案

优化思路

放弃低效的逐组apply,改用全量索引生成+左连接+矢量化填充的方式,利用Pandas的矢量化操作提升效率,同时修复原方法的填充漏洞(原方法无法将仅出现在早期月份的用户填充至后续月份)。

优化代码

import pandas as pd
import os

# 1. 批量读取并合并数据(用pd.concat替代循环append,提升合并效率)
file_list = ['Data_202601.csv', 'Data_202602.csv', 'Data_202603.csv']
dfs = []
for f in file_list:
    df = pd.read_csv(os.path.join(path, f), sep=';')
    df['last_login_timestamp'] = pd.to_datetime(df['last_login_timestamp'], format='%d.%m.%Y %H:%M')
    df['month'] = pd.to_datetime(df['month'], format='%Y%m')
    dfs.append(df)
df_all = pd.concat(dfs, ignore_index=True)
df_all = df_all.sort_values(['customer', 'user_id', 'month']).reset_index(drop=True)

# 2. 生成全量的(customer, user_id, month)组合,确保每个用户在所有月份都有记录
# 获取所有唯一的客户-用户组合
unique_users = df_all[['customer', 'user_id']].drop_duplicates()
# 获取数据覆盖的所有连续月份
min_month = df_all['month'].min()
max_month = df_all['month'].max()
all_months = pd.date_range(start=min_month, end=max_month, freq='MS')
# 生成笛卡尔积:每个用户对应所有月份
unique_users['temp_key'] = 1
months_df = pd.DataFrame({'month': all_months, 'temp_key': 1})
full_index = pd.merge(unique_users, months_df, on='temp_key').drop('temp_key', axis=1)

# 3. 左连接原数据,填充缺失值
# 左连接保留全量索引,缺失的月份会显示NaN
result = full_index.merge(df_all, on=['customer', 'user_id', 'month'], how='left')
# 按客户-用户分组,向前填充所有缺失字段
result = result.groupby(['customer', 'user_id']).ffill().reset_index(drop=True)

优化效果说明

  1. 性能提升:矢量化操作避免了groupby.apply的逐组循环,处理1000万级数据的时间可恢复至接近升级前的水平。
  2. 修复填充漏洞:原方法仅能填充有起始和结束记录的用户(如3333/9),新方法会将仅出现在早期月份的用户(比如仅在2026年1月出现的4444/8)自动填充至所有后续月份,保持其最近的登录数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 15:47:26