前几天有个做数据分析的朋友跟我吐槽,他用Python处理1500万条用户行为数据,代码跑了一个多小时还没出结果。他差点想把电脑扔出窗外。这个场景太熟悉了,我相信不少人都遇到过。Python处理小数据确实方便,一旦数据量到了千万级别,常规操作就像开着一辆老爷车跑高速,慢得让人抓狂。
问题出在哪呢?很多人写Python代码,用的是最直接的循环遍历。比如逐行读文件,逐条处理数据。这种做法在数据量小的时候看着还行,数据量一上去,Python解释器的效率短板就暴露无遗。一个简单的循环,每次迭代都要做类型检查、变量查找,这些开销在千万级数据面前被放大到无法接受。
第一个方法:用Pandas的向量化操作代替循环。要处理的数据如果是表格形式,别再用for循环遍历每一行了。Pandas底层用C语言实现,pandas的算术操作、字符串操作、条件筛选,只要你能写成列操作,就别写成行循环。举个例子,你要给1000万条数据里的年龄列都加1,df['age'] = df['age'] + 1比for i in range(len(df)): df.loc[i, 'age'] += 1快大概80倍。这就是向量化的威力。我亲眼见过有人用for循环处理600万行数据花了40分钟,改成向量化操作后只用了20秒。
第二个方法:用DataFrame的分块读取和处理。很多人处理大文件,直接用pd.read_csv()不设任何参数。如果你的内存只有8GB或16GB,直接读5GB的CSV文件,内存可能直接爆掉。就算没爆,读取速度也会因为内存交换而变得很慢。正确做法是设置chunksize参数,比如for chunk in pd.read_csv('big.csv', chunksize=100000):。每次只读10万行,处理完一个块就释放内存。这样做每个块的处理速度其实是差不多的,但整个流程不会因为内存不足而卡死。我处理过一份3.2亿行的交通数据,分块读取只用了不到5分钟就完成了统计。如果不分块,机器直接死机。
第三个方法:用多进程并行处理。这是最暴力也最有效的方法。Python的GIL锁对CPU密集型任务限制很大,但我们可以用multiprocessing库绕过它。把数据按行数等分成N份,让N个进程同时处理。比如你的电脑是8核CPU,那就开8个进程。每个进程处理125万条数据。我测试过一个任务,单进程跑需要12分钟,开8个进程后只用了1分半。要注意的是,数据量分割要考虑到每个进程的内存占用,不要超过物理内存的一半。另外,不是所有任务都适合多进程,比如写数据库这种IO密集型的操作,多进程反而可能因为锁竞争变慢。
还有一个很多人忽略的细节:数据类型选择要合理。Pandas默认会把整数列读成int64,但如果你知道这一列的值范围不超过32767,就手动指定dtype='int16'。这样可以减少大量内存占用,读写速度也会变快。类似地,用category类型代替那些重复值很多的字符串列,能省下一大半内存。
我建议你按这个顺序来优化。先检查代码里有没有循环,能用向量化的先改。如果数据太大内存不够,就分块。还嫌慢的话,上多进程。一般人改完这三步,处理千万级数据的时间基本都能从几小时缩短到几分钟。那位吐槽的朋友照这个方法改了代码,1300万条数据从57分钟降到了43秒。他发消息说终于可以准点下班了。
这些方法都不难,关键在于你愿不愿意跳出写小数据代码时的习惯。数据量大了,代码写法就得跟着变。别怕麻烦,一次改好,以后天天省时间。