含lambda的Python数据处理代码运行极慢 如何有效优化提速
性能瓶颈根因
- 99%的耗时来自网络IO:你调用的
forex_python.converter.CurrencyRates.convert()每次执行都会发起HTTP请求拉取对应日期的汇率,70行数据对应70次网络请求,单次请求延迟300~500ms的话总耗时自然到30秒级。你之前试的numba只优化CPU计算逻辑,完全碰不到IO环节;multiprocessing没提速基本是因为没做会话复用、被目标接口限流,或者进程启动开销盖过了并行收益,根本没摸到瓶颈。 - 剩下1%的耗时来自pandas的低效用法:按国家拆分df再合并做了大量无意义内存拷贝,
apply(axis=1)逐行遍历的效率比pandas原生向量化操作低1~2个数量级。 cpi.inflate逐行重复查表:相同年月的通胀系数是固定值,逐行调用会重复查静态表做重复计算。
优化方案(按收益从高到低排序)
- 第一优先级:干掉逐行网络请求,把IO量降4个数量级
你现在的逻辑是每算一行发一次请求,7万行就是7万次请求,按单次300ms算要跑近6个小时,完全不可行。
正确做法是:- 先扫描全量数据,提取所有出现过的日期、需要转换的源货币(你的场景只有EUR、GBP两种)
- 一次性拉取这些日期对应对USD的汇率,存成
(源货币, 日期) -> 汇率的映射表,或者直接下载欧央行/美联储公开的历史汇率全量CSV到本地,后续直接读本地文件,连网络请求都不用发。
这一步做完,网络请求从7万次降到最多几千次(按你数据覆盖的时间跨度算,跨10年也就3000多个日期),如果用本地静态汇率文件,IO开销直接降到毫秒级。
- 第二优先级:替换逐行apply为向量化操作,去掉无意义的df拆分
不要拆成DE/GB/US三个子df再拼接,直接用pandas原生的map、merge接口做批量处理:- 建国家到货币的映射字典,直接用
Series.map()给全量df打源货币标签 - 把存好的汇率表转成和原df同结构的表,用
pd.merge()按源货币、日期做左连接,一次性把所有行的汇率匹配上,直接做乘法算USD金额,完全不用逐行apply - 美国行的源货币是USD,汇率固定为1,不用走转换逻辑
- 建国家到货币的映射字典,直接用
- 第三优先级:给cpi通胀计算加缓存,避免重复计算
cpi的通胀数据是本地静态存储的,同一个日期对应的通胀系数完全固定。先把所有出现过的日期去重,提前算好每个日期对应的通胀系数存成映射表,再用Series.map()批量匹配,不要逐行调用cpi.inflate。
优化后参考代码框架
import cpi import pandas as pd # 第一次运行时批量拉取所需汇率,存为本地csv,格式为[source_currency, date, rate_to_usd] # USD对应的rate_to_usd固定填1即可,后续运行直接读本地文件,无需网络请求 rate_df = pd.read_csv("historical_usd_exchange_rates.csv", parse_dates=['date']) rate_df['date'] = rate_df['date'].dt.date # 读数据时只加载需要的列,减少内存占用和IO耗时 transaction_df = pd.read_csv( "tickets.csv", usecols=['customer_country_code', 'processed_at', 'amount'] ) transaction_df['date'] = pd.to_datetime(transaction_df['processed_at']).dt.date # 批量映射国家对应货币,无需拆分df country_currency_map = {'DE': 'EUR', 'GB': 'GBP', 'US': 'USD'} transaction_df['source_currency'] = transaction_df['customer_country_code'].map(country_currency_map) # 批量匹配汇率,计算美元金额 transaction_df = transaction_df.merge(rate_df, on=['source_currency', 'date'], how='left') transaction_df['amount_usd'] = transaction_df['amount'] * transaction_df['rate_to_usd'] # 批量匹配通胀系数,计算实际美元价值 unique_dates = transaction_df['date'].unique() inflation_map = {d: cpi.inflate(1, d) for d in unique_dates} transaction_df['inflation_factor'] = transaction_df['date'].map(inflation_map) transaction_df['Real_USD'] = transaction_df['amount_usd'] * transaction_df['inflation_factor']
额外说明
- 这套优化跑7万行数据总耗时基本在1秒以内,不需要用numba或者multiprocessing,这两个工具都是解决CPU密集型瓶颈的,你的场景瓶颈在IO和pandas用法不对,用这两个工具纯属方向错了。
- 日期转换直接用pandas的
.dt接口,不要套apply,原生向量化实现比逐行apply快几十倍。 - 如果需要定期更新汇率,写个单独的脚本每周/每月拉一次增量汇率更新到本地汇率文件就行,不要在主处理逻辑里发请求。
内容的提问来源于stack exchange,提问作者user9355500
相关产品推荐
相关产品推荐

