处理大型Pandas DataFrame时Python代码挂起,CPU使用率降至0%
嘿,我之前也踩过类似的超大型DataFrame坑,你的情况太典型了——本质是物理内存耗尽后,系统被迫依赖虚拟内存(硬盘)进行数据交换,而硬盘速度比内存慢几个数量级,导致进程看起来像是“停滞”了。CPU使用率为0%是因为进程一直在等内存数据从硬盘读入,磁盘使用率显示0可能是系统交换过程中暂时的停顿,或者资源监视器没捕捉到持续的小量读写。下面给你分应急处理和长期优化两个方向梳理解决办法:
一、先救当前停滞的进程(应急方案)
如果你的Python进程还没崩溃,别着急强行关:
- 立刻关掉所有其他占内存的程序(比如浏览器、IDE的其他窗口),给Python腾物理内存;
- 打开任务管理器,找到Python进程,右键调优先级为「高」,让系统优先给它分配资源;
- 要是你开了虚拟内存,确保它的大小足够(建议设为物理内存的1.5-2倍),不过这只是权宜之计,等待时间可能还是很长,做好心理准备。
二、找到内存瓶颈的根源
先搞清楚你在执行什么操作?不同操作的内存需求天差地别:
merge、join这类操作要构建哈希表,内存占用可能是原始DataFrame的2-3倍;sort_values需要额外内存存临时排序数据;- 哪怕是
apply(尤其是逐行apply),也会因为生成大量中间数据占满内存。
先跑个命令看看当前DataFrame的真实内存占用:
print(df.memory_usage(deep=True).sum() / (1024**3)) # 输出总内存(GB)
如果这个数值已经接近你的物理内存,那肯定是内存不够了。
三、长期优化:从根源降低内存占用
1. 给列换更高效的数据类型
Pandas默认会给列分配冗余的类型,比如把int64改成int32(如果数值范围在-2147483648到2147483647之间),低基数的字符串列换成category类型,能直接砍半甚至砍掉90%的内存:
# 转换整数列 df['user_id'] = df['user_id'].astype('int32') # 把重复率高的字符串列转成category df['product_type'] = df['product_type'].astype('category') # 浮点列如果不需要高精度,转成float32 df['price'] = df['price'].astype('float32')
2. 分块处理,不一次性加载全量数据
用read_csv的chunksize参数分块读,每次处理一小部分数据,避免把整个6GB的DataFrame塞进内存:
chunk_size = 1_000_000 # 每次处理100万行 output_path = 'processed_data.csv' # 先写表头 pd.read_csv('large_data.csv', nrows=0).to_csv(output_path, index=False) # 分块处理并追加结果 for chunk in pd.read_csv('large_data.csv', chunksize=chunk_size): # 这里写你的操作,比如筛选、计算 filtered_chunk = chunk[chunk['sales'] > 1000] # 追加到结果文件 filtered_chunk.to_csv(output_path, mode='a', header=False, index=False)
3. 用Dask替代Pandas处理超大型数据
如果分块处理太麻烦,直接上Dask——它是为大数据设计的并行框架,语法和Pandas几乎一样,但会自动分块并行处理,不需要你手动管理内存:
import dask.dataframe as dd # 读取数据,和Pandas几乎一样 ddf = dd.read_csv('large_data.csv') # 执行操作,比如分组求和 result = ddf.groupby('region')['sales'].sum() # 计算结果(如果结果能放进内存就转成Pandas,不行就直接写文件) result.compute().to_csv('region_sales.csv')
4. 避开内存密集型操作
- 尽量用Pandas的矢量化操作,别用逐行
apply——矢量化操作是底层优化的,内存效率高得多; - 合并数据前先过滤掉不需要的列和行,减少参与合并的数据量;
- 排序时用
sort_values(kind='mergesort'),它的内存占用比默认的quicksort低很多。
四、关于你说的“静置10-15分钟”
这种情况就是系统在做内存交换(swap):物理内存满了之后,系统把内存里暂时用不到的数据写到硬盘的虚拟内存,等需要的时候再读回来。这个过程特别慢,尤其是内存占满的时候,系统会反复交换数据,导致CPU和磁盘看起来没动静——其实系统全在忙交换,没多余资源响应。等足够久可能会完成,但效率极低,不如从根源优化内存使用。
内容的提问来源于stack exchange,提问作者Chaos

