Python大表格转置:比zip数组更高效的内存优化方案?
嘿,我太懂你这种内存爆掉的崩溃感了——200万行300列的表格,直接全塞进内存转置,Python不报错才怪!咱们先拆解下你原代码的问题,再给你几个高效的解决方案:
先说说你原代码的核心问题
你的代码里f.read()直接把整个文件一次性读进内存,然后转成列表的列表m,这对于超大文件来说内存开销爆炸:光是200万×300个字符串对象,加上Python列表的额外存储成本,轻松就能把几G内存吃光。咱们要做的就是避免把整个数据集加载到内存,换成分批/逐行处理的思路。
解决方案1:逐行处理+临时文件(最低内存占用)
这个方法全程不需要把所有数据存进内存,而是边读原文件,边把每一列的内容写到单独的临时文件里,最后再把这些临时文件按行合并(转置后的每一行,就是原文件的每一列)。内存占用几乎可以忽略不计,适合内存特别紧张的集群环境。
import sys import os separator = "\t" input_file = sys.argv[1] output_file = sys.argv[2] # 第一步:先获取总列数 with open(input_file, 'r') as f: first_line = f.readline().strip() col_count = len(first_line.split(separator)) # 第二步:为每一列创建临时文件句柄 col_temp_files = [open(f"./col_{idx}.tmp", 'w', encoding='utf-8') for idx in range(col_count)] # 第三步:逐行读取原文件,把每一列内容写入对应临时文件 with open(input_file, 'r', encoding='utf-8') as f: for line in f: stripped_line = line.strip() if not stripped_line: continue columns = stripped_line.split(separator) # 把当前行的每个元素写入对应列的临时文件 for col_idx, val in enumerate(columns): col_temp_files[col_idx].write(f"{val}\n") # 关闭所有临时文件 for f in col_temp_files: f.close() # 第四步:合并临时文件,生成转置后的结果 with open(output_file, 'w', encoding='utf-8') as out_f: # 循环读取每个临时文件的一行,拼接成转置后的一行 while True: transposed_row = [] # 遍历所有列的临时文件 for col_idx in range(col_count): with open(f"./col_{col_idx}.tmp", 'r', encoding='utf-8') as col_f: line = col_f.readline() if not line: # 某一列读完了,说明所有数据都处理完了 break transposed_row.append(line.strip()) if not transposed_row: break out_f.write(f"{separator.join(transposed_row)}\n") # 清理临时文件 for col_idx in range(col_count): os.remove(f"./col_{col_idx}.tmp")
解决方案2:Pandas分块转置(简洁易实现)
如果你熟悉Pandas,用它的分块读取功能可以快速实现转置,代码比手动写临时文件简洁很多,而且能自动处理各种数据类型。
import pandas as pd import sys separator = "\t" input_file = sys.argv[1] output_file = sys.argv[2] chunk_size = 10000 # 每次处理1万行,根据你的集群内存调整大小 # 初始化结果文件(先清空) with open(output_file, 'w') as f: pass first_chunk = True # 分块读取原文件 for chunk in pd.read_csv(input_file, sep=separator, chunksize=chunk_size, low_memory=False): # 转置当前分块 transposed_chunk = chunk.T # 追加写入结果文件:第一块保留表头,后续块不写 transposed_chunk.to_csv(output_file, sep=separator, mode='a', header=first_chunk, index=False) first_chunk = False
解决方案3:用Dask处理超大数据(全自动分块)
如果你的集群支持并行计算,Dask是处理超大数据的绝佳选择——它会自动把数据分成小块,并行处理,完全不需要你手动管理内存,代码极简。
import dask.dataframe as dd import sys separator = "\t" input_file = sys.argv[1] output_file = sys.argv[2] # 用Dask读取TSV文件(自动分块) df = dd.read_csv(input_file, sep=separator) # 转置数据 transposed_df = df.T # 保存为单个转置后的文件 transposed_df.to_csv(output_file, sep=separator, single_file=True, index=False)
额外技巧:纯数值型数据用NumPy内存映射
如果你的表格里全是数值(整数/浮点数),可以用NumPy的内存映射文件,它把数据存在磁盘上,只在需要时加载部分到内存,转置效率极高:
import numpy as np import sys import os separator = "\t" input_file = sys.argv[1] output_file = sys.argv[2] # 先统计总行数和列数 row_count = sum(1 for line in open(input_file, encoding='utf-8') if line.strip()) with open(input_file, 'r', encoding='utf-8') as f: col_count = len(f.readline().strip().split(separator)) # 创建内存映射数组,dtype根据你的数据类型调整(比如float64/int32) mmap = np.memmap('temp_data.dat', dtype='float64', mode='w+', shape=(row_count, col_count)) # 逐行读取写入内存映射 with open(input_file, 'r', encoding='utf-8') as f: for row_idx, line in enumerate(f): stripped_line = line.strip() if not stripped_line: continue values = [float(val) for val in stripped_line.split(separator)] mmap[row_idx] = values # 转置并保存结果 transposed_mmap = mmap.T np.savetxt(output_file, transposed_mmap, fmt='%.6f', delimiter=separator) # 删除临时内存映射文件 os.remove('temp_data.dat')
内容的提问来源于stack exchange,提问作者Jared
相关产品推荐
相关产品推荐

