Pandas计算员工工时出现MemoryError内存错误的原因及优化方案
问题原因分析
极小数据量触发内存报错的核心原因有两点:
- 首要问题是索引越界漏洞:
work_time_per_employee_per_process函数中的循环变量遍历范围是range(len(input_df)),当循环到输入表的最后一行时,integer + 1已经超出了数据行的索引范围。部分旧版本Pandas遇到这类越界取值场景时,不会直接抛出索引错误,而是会尝试生成填充缺失值的扩展数组,进而触发无限制的内存申请,哪怕原始数据只有几十行也会触发内存溢出。 - 次要问题是低效的append操作:Pandas的
DataFrame.append是不可变操作,每次调用都会完整复制现有DataFrame生成新对象,虽然小数据量下不会直接导致内存爆炸,但叠加索引越界的异常场景会进一步放大内存消耗。
代码优化方案
直接弃用嵌套循环+逐行append的实现方式,改用Pandas原生向量化运算,既修复索引越界问题,也大幅降低内存消耗,优化后代码如下:
import pandas as pd def working_time_per_employee(dataframe): # 拷贝原始数据避免修改原表 df = dataframe.copy() # 预转换时间戳为datetime类型,避免重复转换开销 df['action_timestamp'] = pd.to_datetime(df['action_timestamp']) # 按流程ID+操作时间排序,保证操作顺序正确 df = df.sort_values(['process_id', 'action_timestamp']).reset_index(drop=True) # 按流程分组,取每行对应的下一行操作、操作人、时间戳,越界行自动填充NaN df['next_action'] = df.groupby('process_id')['action'].shift(-1) df['next_action_user'] = df.groupby('process_id')['action_done_by_username'].shift(-1) df['next_action_ts'] = df.groupby('process_id')['action_timestamp'].shift(-1) # 匹配业务规则条件 cond_start_action = df['action'].isin(['Claim', 'Release']) cond_end_action = df['next_action'].isin(['Back to Claiming Pool', 'Hold', 'Cancelled', 'Complete']) cond_same_user = df['action_done_by_username'] == df['next_action_user'] # 过滤符合条件的行,直接计算工时 result = df[cond_start_action & cond_end_action & cond_same_user].rename(columns={ 'action_done_by_username': 'user_name', 'action_done_by_fullname': 'employee_full_name', 'action_date': 'date' }) result['working_time'] = (result['next_action_ts'] - result['action_timestamp']).dt.total_seconds().astype(int) # 输出指定字段的结果表 return result[['process_id', 'user_name', 'employee_full_name', 'working_time', 'date']].reset_index(drop=True)
优化后代码的优势:
- 完全移除了逐行循环和逐行append操作,无重复复制DataFrame的开销,内存消耗可降低90%以上
- 用分组shift操作取相邻行,越界行自动填充NaN在过滤阶段直接排除,彻底修复索引越界导致的内存异常申请问题
- 增加了排序逻辑,不受原始数据顺序干扰,计算结果准确性更高
内容的提问来源于stack exchange,提问作者Alfaisal Albakri
相关产品推荐
相关产品推荐

