上周四周会,我照例汇报数据看板。运营总监问了一句“这个月北京地区用户行为轨迹跑完要多久”,我说“三秒钟”。他愣了一下,问我是不是吹牛。我把笔记本转过去给他看,屏幕上一个700行的脚本刚刚跑完,处理了109,872行原始数据,耗时2.87秒。周围几个人凑过来看,财务组的老王直接说“你早说啊,我们那套报表天天跑半小时”。
其实之前我也用pandas逐行遍历,十万行数据要跑将近四分钟。后来发现问题的关键不在数据量,在写法。我换了个思路,把循环改成向量化操作。举个例子,要筛选出某个时间范围内的记录,用 df[(df['time'] > start) & (df['time'] < end)] 一次搞定。以前我是写for循环,再往列表里append,那当然慢。
核心就一句话:能用DataFrame方法就别用for循环。 我处理那批用户数据时有个步骤要计算每笔订单的金额占比。以前做法是遍历每一行,拿每个金额除以总和。换成 df['ratio'] = df['amount'] / df['amount'].sum ,这一句就能替代几十行循环代码。速度差距不是一点半点,十万行数据下是毫秒级对秒级的区别。
另一个坑是频繁读写硬盘。我接手的数据工程师留下的脚本,每处理完一个步骤就 to_csv 一次。硬盘读写是内存操作的几百倍速度差距,能不慢吗。 把所有中间结果放在内存里,最后一次性保存到硬盘。 我调整之后,原来跑六分钟的任务压缩到四十秒。
还有个技巧可能你们也知道,就是数据类型优化。pandas读入csv时默认会把整数列当成int64,字符串列当成object。实际上很多字段根本用不了这么高的精度。我试过把内存占用量从1.2G砍到380M,数据读取速度快了将近一倍。用 pd.to_numeric 和 astype 调整类型,代码改动不超过十行。
说回那次周会。我演示完脚本后,运营总监当场让我把这个脚本共享到组里。后来他们用这个改写的 process_batch.py 替换了原来基于excel宏的旧流程。现在每天上午十点数据更新后,整个部门的看板能在十二点前自动刷新完毕。原来要等整个下午。
我其实没用什么高级技术,就是少写循环,善用内建方法。 你们要是手里有大文件处理的任务,先看一眼代码里有没有 for row in df.iterrows 这种写法,有的话果断换成向量操作。再检查下有没有重复的硬盘读写。这两件事做完了,性能至少提升十倍。
那天周会结束后,隔壁组的实习生小刘私下来问我,说他的任务要处理三百万行销售数据,问我有没有建议。我说你先按上面两步改一下,如果还不够快,再用dask或者polars替换pandas。他改完后跟我说,原来跑二十分钟的任务现在不到六秒。他说这是他入职以来第一次在周会上被组长表扬。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.