循环处理千份大CSV性能衰减,求内存高效分块优化方案
优化方案:解决循环拼接DataFrame的性能下降问题
你的问题根源在于每次循环使用pd.concat拼接merged_data时,都会创建新的DataFrame并复制全部已有数据——随着数据量积累,内存占用越来越高,复制时间也会线性增长,这就是前50次快、后续越来越慢的原因。
下面是几个内存高效的处理方式:
1. 用列表暂存小DataFrame,最后一次性拼接
这是最直接的优化,避免每次循环复制大数据集:
import os import pandas as pd # 用列表暂存每个处理后的小DataFrame,而非每次拼接 data_list = [] count = 0 for index, row in df.iterrows(): folder_name = row['File ID'].strip() file_name = row['File Name'].strip() file_path = os.path.join(root_path, folder_name, file_name) # 优化读取与转置步骤:直接指定index_col减少中间操作 file_data = pd.read_csv(file_path, names=['Case', 'value'], sep='\t', index_col='Case') file_data_transposed = file_data.T.reset_index(drop=True) # 插入标识列 file_data_transposed.insert(0, 'folder_file_id', f'{folder_name}_{file_name}') data_list.append(file_data_transposed) count += 1 print(count) # 最后一次性拼接所有数据 merged_data = pd.concat(data_list, axis=0, ignore_index=True)
这种方式内存效率高很多,因为列表只是存储每个小DataFrame的引用,直到最后一步才合并,不会在循环中反复复制大数据。
2. 分块保存到磁盘(超大数据量场景)
如果总数据量远超内存容量,可以每处理一定数量的文件就把当前数据保存到磁盘(优先用Parquet格式,比CSV压缩率高、读写快),最后再合并所有分块:
import os import pandas as pd from glob import glob data_list = [] chunk_size = 50 # 每50个文件存一个分块 chunk_count = 0 root_path = "你的根路径" count = 0 for index, row in df.iterrows(): folder_name = row['File ID'].strip() file_name = row['File Name'].strip() file_path = os.path.join(root_path, folder_name, file_name) file_data = pd.read_csv(file_path, names=['Case', 'value'], sep='\t', index_col='Case') file_data_transposed = file_data.T.reset_index(drop=True) file_data_transposed.insert(0, 'folder_file_id', f'{folder_name}_{file_name}') data_list.append(file_data_transposed) count += 1 print(count) # 达到chunk_size就保存分块 if count % chunk_size == 0: chunk_count += 1 chunk_df = pd.concat(data_list, axis=0, ignore_index=True) chunk_df.to_parquet(f'chunk_{chunk_count}.parquet') data_list = [] # 清空列表释放内存 # 处理剩余不足chunk_size的数据 if data_list: chunk_count += 1 chunk_df = pd.concat(data_list, axis=0, ignore_index=True) chunk_df.to_parquet(f'chunk_{chunk_count}.parquet') # 合并所有分块 chunk_files = glob('chunk_*.parquet') merged_data = pd.concat([pd.read_parquet(f) for f in chunk_files], axis=0, ignore_index=True) # 可选:删除临时分块文件 for f in chunk_files: os.remove(f)
额外优化点
- 读取CSV时尽量减少不必要的操作:比如指定
low_memory=False(如果数据类型复杂),或者用dtype提前指定列类型,避免pandas自动推断类型消耗资源。 - 转置后的列名可以直接在读取时处理,减少后续插入操作的开销。
内容的提问来源于stack exchange,提问作者DeepLearner123
相关产品推荐
相关产品推荐

