如何在df.apply()函数抛出异常时获取已计算的部分结果?
不用必须改用for循环,但逐行迭代是更可靠的方案
当你用df.apply()处理这类耗时且可能出错的API调用时,确实会遇到「中途异常/中断就丢失所有已计算结果」的问题——因为apply()是先完成所有行的计算、生成完整的Series后才会赋值给DataFrame列,中途一旦抛出异常或被中断,之前的计算结果根本没机会写入原DataFrame。
你不一定必须改用for循环,但逐行迭代(比如for循环)是最直接、可控性最强的方案,能实时保存已完成的结果。下面是优化后的代码示例:
import random import time import pandas as pd # 模拟慢速且可能抛出异常的REST API def getApiValue_sim(p1:int, p2:int) -> int: time.sleep(1) # 模拟请求耗时 i = random.randint(p1,p2) if not i%4: raise Exception("Multiple of 4") return i dataframe = pd.DataFrame({'P1':[1,2,3],'P2':[11, 12, 13],'Reply':[pd.NA,pd.NA,pd.NA]}) try: # 逐行迭代处理,只更新未填充的行 for idx, row in dataframe.iterrows(): if pd.isna(row['Reply']): try: # 实时写入结果到DataFrame dataframe.loc[idx, 'Reply'] = getApiValue_sim(row['P1'], row['P2']) except Exception as e: # 捕获单条请求的异常,不终止整个流程 print(f"行{idx}处理失败: {str(e)}") # 可选:添加错误列记录失败原因 dataframe.loc[idx, 'Error'] = str(e) except KeyboardInterrupt: # 处理用户CTRL-C中断的情况 print("程序被用户手动中断") finally: # 无论异常/中断,都输出并保存已完成的结果 print("已完成的结果:") print(dataframe) # 保存到文件,避免数据丢失 dataframe.to_csv('partial_result.csv', index=False)
这个方案的优势:
- 实时更新DataFrame,中途任何异常或中断都不会丢失已处理完的行数据
- 单条请求失败不会阻断整个流程,还能记录错误信息
- 可以跳过已填充的行,方便脚本中断后重启继续处理
如果坚持想用apply(),也可以在每行的处理函数里捕获异常,但这种方式无法解决「CTRL-C中断丢失结果」的问题——因为apply()要等所有行计算完成才会赋值。示例代码如下:
try: def process_row(row): if pd.isna(row['Reply']): try: return getApiValue_sim(row['P1'], row['P2']) except Exception as e: print(f"行{row.name}处理失败: {str(e)}") return pd.NA return row['Reply'] # 只有当所有行计算完成后,才会赋值给Reply列 dataframe['Reply'] = dataframe.apply(process_row, axis=1) except KeyboardInterrupt: print("程序被中断") finally: print(dataframe)
但要注意:如果中途被CTRL-C中断,apply()还没生成完整的结果Series,原DataFrame的Reply列还是初始的全缺失值,之前计算的所有结果都会丢失。
综上,如果你需要确保异常或中断时保留已完成的结果,优先选择逐行迭代的for循环方案。
内容的提问来源于stack exchange,提问作者Maxime Charrière
相关产品推荐
相关产品推荐

