尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
076、Pandas 性能优化:从 iterrows 到 vectorize——100 倍提速的演进
076、Pandas 性能优化从 iterrows 到 vectorize——100 倍提速的演进上周帮团队排查一个数据清洗脚本跑了一小时还没出结果。我盯着终端里跳动的光标心里大概有数了——八成又是哪个哥们儿在DataFrame上写了循环。打开代码一看果然一个iterrows套着两层if-else处理50万行数据每行还要做字符串拼接和条件判断。这种写法不慢才怪。我直接动手重写把循环改成向量化操作跑完只用了18秒。旁边的新人瞪大了眼睛问我是不是换了台服务器。我说没有只是把代码从“手动挡”换成了“自动挡”。先看一个典型的“慢代码”长什么样假设我们要处理一个销售订单表根据金额和地区计算折扣后的价格。很多人会这样写importpandasaspdimportnumpyasnp dfpd.DataFrame({amount:np.random.uniform(100,10000,500000),region:np.random.choice([华东,华北,华南,西南],500000)})defcalculate_discount(row):ifrow[region]华东:ifrow[amount]5000:returnrow[amount]*0.85else:returnrow[amount]*0.9elifrow[region]华北:returnrow[amount]*0.88else:returnrow[amount]*0.95# 别这样写慢到怀疑人生df[discounted]df.apply(calculate_discount,axis1)这段代码跑50万行在我的机器上大概需要12秒。看起来还行别急如果换成iterrows直接奔着40秒去了。更可怕的是如果业务逻辑再复杂一点比如嵌套几个字典查找、正则匹配半小时都跑不完。为什么循环和apply这么慢这里踩过坑的人都知道iterrows返回的是Series对象每次迭代都要做类型推断、索引对齐Python解释器在每一行都要重新进入Pandas的C扩展层。apply虽然看起来高级一点本质上还是在Python层面逐行调用函数没有利用到NumPy底层的向量化能力。打个比方循环就像你一个一个地搬砖向量化操作就像用铲车一次性铲起一堆砖。CPU的SIMD指令集就是那把铲车但前提是你得把数据组织成它能理解的形式。第一步用向量化操作替换条件逻辑对于上面的折扣计算最直接的做法是用np.select或者布尔索引# 这才是正确姿势conditions[(df[region]华东)(df[amount]5000),(df[region]华东)(df[amount]5000),(df[region]华北)]choices[df[amount]*0.85,df[amount]*0.9,df[amount]*0.88]# 默认值给0.95df[discounted]np.select(conditions,choices,defaultdf[amount]*0.95)这段代码跑完只需要0.3秒。40倍提速而且代码更短、更清晰。np.select会一次性生成所有条件的布尔掩码然后通过C级别的循环完成赋值完全没有Python层面的逐行开销。第二步字符串操作也要向量化很多人处理字符串时习惯用apply加lambda# 慢别这样写df[clean_region]df[region].apply(lambdax:x.replace(华,中))换成Pandas自带的字符串方法df[clean_region]df[region].str.replace(华,中)str访问器背后调用的是NumPy的向量化字符串操作速度能快10倍以上。如果要做正则匹配用str.contains、str.extract不要自己写循环。第三步groupby之后别用apply分组聚合是另一个重灾区。很多人习惯这样写# 慢别这样写resultdf.groupby(region).apply(lambdag:g[amount].sum()/g[amount].count())直接用聚合函数resultdf.groupby(region)[amount].agg([sum,count])result[avg]result[sum]/result[count]或者更简洁的resultdf.groupby(region)[amount].mean()groupby的聚合操作是高度优化的C代码而apply会把每个分组的数据传到Python层来回切换上下文性能损失巨大。第四步终极武器——用NumPy的ufunc如果Pandas没有提供你需要的向量化函数别急着写循环。看看能不能用NumPy的通用函数ufunc组合实现。比如我们要对金额做分段标记小于1000为“低”1000-5000为“中”大于5000为“高”。# 用np.selectbins[0,1000,5000,np.inf]labels[低,中,高]df[level]np.select([df[amount]1000,(df[amount]1000)(df[amount]5000),df[amount]5000],labels)或者用pd.cut但注意pd.cut内部也是向量化的df[level]pd.cut(df[amount],bins[0,1000,5000,np.inf],labels[低,中,高])什么时候真的需要用循环说了这么多向量化的好处但有些场景确实绕不开循环。比如每一行的计算依赖上一行的结果比如累计收益计算需要调用外部API或数据库网络IO无法向量化复杂的业务规则无法用简单的数学表达式描述对于第一种情况可以用numba加速。Pandas 0.24之后支持pd.Series.rolling配合apply但更推荐用numba的JIT编译fromnumbaimportjitjit(nopythonTrue)defcumulative_returns(returns):cum1.0resultnp.empty_like(returns)fori,rinenumerate(returns):cum*(1r)result[i]cumreturnresult df[cum_return]cumulative_returns(df[daily_return].values)numba会把Python循环编译成机器码速度接近C语言。但注意numba只支持NumPy数组和基本Python类型不能直接传DataFrame。一个真实的优化案例上个月处理一个用户行为日志200万行需要根据时间戳和用户ID计算会话间隔。原始代码用iterrows嵌套groupby跑了25分钟。我重构后用了三步先用sort_values按用户和时间排序用groupby加shift获取上一行时间戳向量化计算时间差最终代码跑了2.3秒。那个同事后来跟我说他以为Pandas就只能那么慢。个人经验总结别把Pandas当成Excel的Python版本来用。Pandas的底层是NumPy和C扩展它的设计哲学就是“批量操作”。你每写一个for row in df.iterrows()就是在强迫Pandas放弃它的优势退化成纯Python的速度。调试性能问题时我的习惯是先跑一个df.info()看数据类型确保没有object类型的数值列。然后用%timeit测试关键操作如果单次操作超过1秒就考虑向量化。如果向量化实在做不到再考虑numba或者cython。最后说一句不要为了炫技而用向量化。如果数据量只有几千行循环完全够用没必要为了追求极致性能把代码写成天书。但如果你在处理几十万行以上的数据向量化不是可选项是必选项。
RELATED

相关推荐

OWASP Top 10 深度解析:从原理到实战,构建Web应用安全防线

OWASP Top 10 深度解析:从原理到实战,构建Web应用安全防线

1. 项目概述:为什么每个开发者都必须懂OWASP Top 10?如果你是一名Web开发者、安全工程师,或者哪怕只是对构建安全的线上应用感兴趣,那么“OWASP Top 10”这个词组,你一定听过无数次了。它就像一个安全领域的“流行金曲…

📅 2026/9/16 21:33:21
Mermaid终极指南:从文本到专业图表的完整解决方案

Mermaid终极指南:从文本到专业图表的完整解决方案

Mermaid终极指南:从文本到专业图表的完整解决方案 【免费下载链接】mermaid Generation of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown 项目地址: https://gitcode.com/GitHub_Trending/me/mermaid 还在为技…

📅 2026/9/30 13:12:44
3步掌握大麦抢票脚本:告别黄牛票的终极指南

3步掌握大麦抢票脚本:告别黄牛票的终极指南

3步掌握大麦抢票脚本:告别黄牛票的终极指南 【免费下载链接】DamaiHelper 大麦网演唱会演出抢票脚本。 项目地址: https://gitcode.com/gh_mirrors/dama/DamaiHelper 还在为抢不到心仪的演唱会门票而烦恼吗?大麦抢票脚本DamaiHelper是你的最佳解决…

📅 2026/9/19 20:21:31
MORE NEWS

更多资讯

📰

教师私有知识库搭建指南:RAG原理与实操全流程

1. 教师做私有知识库,到底在解决什么问题先把话说透:教师建私有知识库,不是为了赶AI的时髦,而是为了解决一个非常具体的痛点——你自己的教学资产,散落在十几个地方,用的时候找不到,找到了又没法…

📰

手语识别毕设实战:PyTorch+CNN-LSTM+PyQt5全流程落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

开源WMS选型实战指南:从技术架构到生产落地

1. 这不是“软件推荐列表”,而是一份仓库管理者用血汗换来的开源WMS选型实战指南你点开这个标题,大概率正被三件事压得喘不过气:库存数据对不上账、拣货员在货架间绕圈找货、系统一到月底结账就卡死——这些不是IT问题,是每天真实…

📰

原生 JavaScript 实现密码框小眼睛:光标恢复与无障碍

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Minecraft服务器优化:自制模组解决Create卡顿与深暗之域性能问题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

STM32工程文件管理实战:Keil5新建文件与头文件路径配置详解

很多刚开始用STM32的朋友,最容易卡住的地方往往不是CubeMX里怎么配置引脚,也不是代码逻辑怎么写,反而是最不起眼的工程文件管理:Keil5里怎么新建一个文件、怎么把文件加进工程、为什么加了文件还是报错找不到头文件。这个环节看起…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬