读取5万份TXT后拼接Pandas DataFrame过慢,求优化方法
优化5万份TXT文件转Pandas DataFrame的方案
原代码的核心性能瓶颈有三个:循环中反复调用pd.concat导致大量内存拷贝、单线程处理未利用IO并行能力、read_fwf的固定宽度解析开销较高。以下是针对性的优化方案:
1. 避免循环concat,批量合并DataFrame
每次调用pd.concat都会创建新的DataFrame对象,5万次循环会产生巨量的内存碎片和拷贝开销。改为先收集所有小DataFrame到列表,最后一次性合并,能大幅降低时间消耗。
import glob import pandas as pd folder_path = '/drive/My Drive/dataset/train' file_list = glob.glob(folder_path + "/*.txt") def read_clean_df(file_name) -> pd.DataFrame: df = pd.read_fwf(file_name, header=None) df = df.drop(df.index[19]) df = df.T df.columns = df.iloc[0] df = df[1:] df.reset_index(drop=True, inplace=True) return df # 先收集所有文件的DataFrame到列表 df_collection = [] for file in file_list: df_collection.append(read_clean_df(file)) # 一次性合并所有DataFrame train_df = pd.concat(df_collection, axis=0, ignore_index=True) print(train_df.head(30))
2. 并行处理文件读取
文件读取属于IO密集型任务,单线程会等待IO操作完成后才处理下一个文件。用多线程并行处理可以同时读取多个文件,把等待IO的时间利用起来。
import glob import pandas as pd from concurrent.futures import ThreadPoolExecutor folder_path = '/drive/My Drive/dataset/train' file_list = glob.glob(folder_path + "/*.txt") def read_clean_df(file_name) -> pd.DataFrame: df = pd.read_fwf(file_name, header=None) df = df.drop(df.index[19]) df = df.T df.columns = df.iloc[0] df = df[1:] df.reset_index(drop=True, inplace=True) return df # 用线程池并行处理,max_workers根据你的机器配置调整(建议8-16) with ThreadPoolExecutor(max_workers=12) as executor: df_collection = list(executor.map(read_clean_df, file_list)) train_df = pd.concat(df_collection, axis=0, ignore_index=True) print(train_df.head(30))
3. 替换read_fwf为手动文本解析
pd.read_fwf的固定宽度解析逻辑较重,如果你的TXT文件结构规则(比如每行是键值对),可以手动读取文本并处理,比调用read_fwf更快。假设你的文件每行是字段名+值的结构,示例如下:
import glob import pandas as pd folder_path = '/drive/My Drive/dataset/train' file_list = glob.glob(folder_path + "/*.txt") def read_clean_df(file_name) -> pd.DataFrame: # 读取文件并过滤空行 with open(file_name, 'r', encoding='utf-8') as f: lines = [line.strip() for line in f if line.strip()] # 删除第19行(索引18,从0开始计数) del lines[18] # 拆分每行的键和值(适配空格分隔或固定宽度,这里以空格分隔为例) record = {} for line in lines: # 按第一个空格拆分键和值,避免值包含空格的情况 key, value = line.split(maxsplit=1) record[key.strip()] = value.strip() # 返回单行DataFrame return pd.DataFrame([record]) # 结合并行处理使用效果最佳 from concurrent.futures import ThreadPoolExecutor with ThreadPoolExecutor(max_workers=12) as executor: df_collection = list(executor.map(read_clean_df, file_list)) train_df = pd.concat(df_collection, axis=0, ignore_index=True) print(train_df.head(30))
4. 指定数据类型减少内存占用
如果提前知道各字段的数据类型,在创建DataFrame时指定类型,可以大幅降低内存占用,进而提升合并和后续处理的速度。例如:
def read_clean_df(file_name) -> pd.DataFrame: # ... 前面的文本读取逻辑 ... # 预定义数据类型 dtype_map = { 'age': int, 'score': float, 'category': 'category' } return pd.DataFrame([record], dtype=dtype_map)
优化效果说明
- 方案1+2结合,通常能把处理时间从小时级压缩到分钟级;
- 方案3进一步优化单文件读取速度,适合结构规则的文本文件;
- 方案4在数据量极大时能避免内存溢出问题。
内容的提问来源于stack exchange,提问作者ggcoder
相关产品推荐
相关产品推荐

