如何避免内存开销过大的列表推导式?DataFrame处理优化求助
解决方案
一、搞定map返回空列表的问题
为啥会空?因为Python的map返回的是迭代器,只能遍历一次——你第一次调用list(mO)把迭代器里的元素取完后,再调用list(mO)自然就空了,后续的zip根本拿不到数据。
修正后的正确写法
别提前把map转成list,而是把整个处理流程改成单批次处理,每个输入元素从初始到最终结果一次性走完,避免重复使用同一个迭代器:
import itertools import pandas as pd import numpy as np import gc # 先修正F2里的笔误:原代码里的N_f是打错了,应该是N def F1(a): a = pd.DataFrame(np.vstack([a.columns, a])) a = a.astype('int') return a def F2(d): N = pd.DataFrame(d) # 用pandas原生abs()代替列表推导,更快更省内存 N.iloc[0, :] = N.iloc[0, :].abs() N.columns = N.iloc[0] N = N.iloc[1:] return N def F3(d, c): # 重点提醒:7000列的permutations会生成7000*6999=近5000万列,这是内存炸锅的核心! # 如果业务逻辑允许,一定要改这里,不然再怎么优化内存都顶不住 comb = itertools.permutations(d.columns, 2) # 用生成器表达式代替list,避免一次性加载所有组合的DataFrame df_gen = (d[a] + d[b] for a, b in comb) N = pd.concat(df_gen, axis=1) N.columns = N.iloc[0] N = N.iloc[1:] N.columns = c.columns return N def F4(a, b, d): # 全用pandas向量化操作,别搞逐元素处理 Ch = a ** 2 Nu = Ch - b De = b * (b - 1) P = Nu / De P = P.sort_index(axis=1) H = P.groupby(level=0, axis=1).sum() H1 = H / d.iloc[0, -1] return H1 # 把单个输入元素的所有处理步骤打包成一个函数 def process_single_item(item): mo = F1(item) diff = F2(mo) somm = F3(mo, diff) final = F4(diff, somm, mo) return final # 用map生成迭代器,按需生成结果,不一次性存储所有DataFrame final_iter = map(process_single_item, list1) # 逐个遍历处理并输出,避免内存堆积 for idx, result_df in enumerate(final_iter): result_df.to_csv(f"final_result_{idx}.csv", index=False) # 手动清理内存,防止溢出 del result_df gc.collect()
二、解决内存超限的核心优化
1. 别一次性存储所有结果
不管用列表推导还是map,只要把所有处理后的DataFrame都存在内存里,内存肯定炸。改成逐个处理、逐个输出(存文件/数据库),处理完就释放内存,不要留在内存中。
2. 必须优化F3函数(内存重灾区)
7000列的排列组合会生成近5000万列,完全超出常规内存承受范围。你得重新审视业务逻辑:
- 如果最终是要按列名的level0分组求和,能不能直接计算每个分组的总和,跳过生成所有两两组合的步骤?
- 举个例子:假设列名是
['group1_col1', 'group1_col2', 'group2_col1'],两两相加后再按group求和,其实等价于每个group的列数乘以该group的总和,这样就能直接计算,不用生成所有组合。
3. 函数内的细节优化
- 用pandas的向量化操作代替列表推导,比如F2里的
N.iloc[0,:].abs()比列表推导省内存还快。 - 减少不必要的内存占用:比如用
astype('int32')代替默认的int64,能减半整数类型的内存开销;避免创建不必要的中间DataFrame。 - 手动回收内存:处理完一个DataFrame后,用
del删除变量,再调用gc.collect()强制回收内存。
内容的提问来源于stack exchange,提问作者Gigi
相关产品推荐
相关产品推荐

