大尺寸光谱CSV数据文件高效转置的最优实现方案
问题
我有多个GB级的制表符分隔CSV文件,表格结构为3059行、40-80k列,用于光谱数据的图像构建。文件格式如下(第一列是x轴值,后续每列对应一个像素的关联值):
900.00 0.000 ... 901.00 0.030 ... ... ... ... 3999.00 0.801 ... 4000.00 0.798 ...
我自己编写了一套基于列操作的数据处理库来处理这类表格。最近需要从一种原生格式转换数据,该格式中,非x轴值以长逗号分隔字符串存储(包含数十万个数据项),格式如下:
(0.0234,0.0021,...,0.4120,0.3034)
目前我已能提取数据并写入文件,但受限于写入顺序(只能从左到右、从上到下),生成的是行基数据文件。由于现有库是列基的,我需要将文件内容转置(列变行)以适配现有代码,而非重构库。我写的转置代码如下:
def FileRotate(inputfile): global outputfile outputfile = targetFiles[fileIndex] + "_final.dpt" # outputfile will look like "50_micronsample.txt_final.dpt" with open(outputfile, 'w') as output_file: outputcsv = csv.writer(output_file, delimiter = '\t') ncols = len(pd.read_csv(inputfile, nrows=1, encoding='utf-8', delimiter='\t').columns) print(f'\nNumber of columns which will become number of rows: {ncols}') for i in (iterable :=list(range(0,ncols))): columntemp = pd.read_csv(inputfile, usecols=[i], encoding='utf-8', delimiter='\t', header=None) outputcsv.writerow(columntemp.iloc[:,0]) if i < iterable[-1]: print(f'Column {i} has successfully been written. Beginning column {i+1}') elif i == iterable[-1]: print(f'\nColumn {i} has successfully been written. File is complete.')
作为编程新手,我知道这段代码效率极低,转置操作最长需要数小时。请问有没有更高效的实现方式或新思路?注:打印语句用于进度跟踪,outputfile用全局变量是为了适配其他未展示的代码。
优化方案
1. 一次性读入后转置写入(最直接的优化)
你的代码效率低的核心原因是循环读取文件几十次(每读取一列就要重新打开并读取一次整个文件),对GB级文件来说IO开销极大。直接用pandas一次性读入整个文件,转置后再写入,能把IO操作从几十次降到2次(读1次、写1次),效率会提升几个数量级。
优化后的代码:
import pandas as pd import csv def FileRotate(inputfile): global outputfile outputfile = targetFiles[fileIndex] + "_final.dpt" # 一次性读入整个文件,设置header=None避免把第一行当表头 df = pd.read_csv(inputfile, delimiter='\t', encoding='utf-8', header=None) # 转置数据框 df_transposed = df.T total_rows = df_transposed.shape[0] print(f'\nTotal rows to write (original columns): {total_rows}') # 写入转置后的文件 with open(outputfile, 'w', encoding='utf-8') as output_file: writer = csv.writer(output_file, delimiter='\t') # 逐行写入,每处理1000行打印一次进度(避免频繁输出拖慢速度) for idx, row in enumerate(df_transposed.itertuples(index=False, name=None)): writer.writerow(row) if (idx + 1) % 1000 == 0: print(f'Completed writing {idx + 1}/{total_rows} rows') print(f'\nAll rows written. File {outputfile} is complete.')
2. 内存优化:分块处理(内存不足以一次性读入时)
如果你的内存无法容纳整个GB级文件,可以用分块读入+逐块转置拼接的方式,避免一次性占用过多内存:
import pandas as pd import csv def FileRotate(inputfile): global outputfile outputfile = targetFiles[fileIndex] + "_final.dpt" # 设置分块大小,比如每次读1000行 chunk_size = 1000 chunks = [] print(f'\nReading file in chunks of {chunk_size} rows...') for chunk in pd.read_csv(inputfile, delimiter='\t', encoding='utf-8', header=None, chunksize=chunk_size): chunks.append(chunk.T) # 拼接所有转置后的块 df_transposed = pd.concat(chunks, axis=1) total_rows = df_transposed.shape[0] print(f'\nTotal rows to write: {total_rows}') with open(outputfile, 'w', encoding='utf-8') as output_file: writer = csv.writer(output_file, delimiter='\t') for idx, row in enumerate(df_transposed.itertuples(index=False, name=None)): writer.writerow(row) if (idx + 1) % 1000 == 0: print(f'Completed writing {idx + 1}/{total_rows} rows') print(f'\nFile {outputfile} is complete.')
3. 从根源避免转置:生成数据时直接按列基格式写入
既然你是从原生格式提取数据生成行基文件,不如跳过生成行基文件的步骤,直接在提取数据时就按列基格式写入,彻底省去转置开销:
- 原生格式的每个长逗号分隔字符串对应目标文件的一行(原表格的一列)
- 先把x轴值读入列表
x_values,然后将每个原生字符串拆分为数值列表pixel_values,把x_values和pixel_values一一对应后逐行写入 - 比如第一行写x轴值,后续每行写一个像素的所有关联值(对应原表格的一列)
这种方式能把整个流程的IO和计算量降到最低,是最优解。
内容的提问来源于stack exchange,提问作者Levi Friss
相关产品推荐
相关产品推荐

