基于文件名分组合并文件时,循环拼接DataFrame速度极慢求优化
提速优化方案与代码修正
先修正你的生成器问题
你第二段代码生成generator是因为没给列表推导式加方括号,导致pd.read_csv(...) for fn in v是生成器表达式,而非DataFrame列表。如果要沿用这个思路,应改成:
dict_df2 = {} for k, v in dd.items(): # 把生成器改为列表推导式,收集所有子DataFrame df_list = [pd.read_csv(fn , parse_dates=['time'] , dayfirst=True , skiprows=4 , sep="\t" , decimal=',' ).assign(Path=fn) for fn in v] dict_df2[k] = pd.concat(df_list, ignore_index=True)
这和你最初的逻辑一致,但写法更清晰。
提速优化方法
1. 预定义read_csv参数,减少重复开销
把read_csv的参数打包成字典,避免每次调用重复编写,同时减少参数对象的重复创建:
read_params = { 'parse_dates': ['time'], 'dayfirst': True, 'skiprows': 4, 'sep': '\t', 'decimal': ',' } dict_df = {} for k, v in dd.items(): df_list = [pd.read_csv(fn, **read_params).assign(Path=fn) for fn in v] dict_df[k] = pd.concat(df_list, ignore_index=True)
2. 指定数据类型(dtype),避免自动推断
pandas自动推断列类型会消耗大量时间,提前指定非日期列的dtype可大幅提速:
比如假设数据里有value列是浮点型、id列是字符串:
read_params = { 'parse_dates': ['time'], 'dayfirst': True, 'skiprows': 4, 'sep': '\t', 'decimal': ',', 'dtype': {'value': float, 'id': str} # 按需添加你的列类型 }
3. 并行读取文件(IO密集型任务最优解)
读取文件属于IO密集型操作,用多进程并行处理可充分利用资源,大幅缩短时间。示例用concurrent.futures:
from concurrent.futures import ProcessPoolExecutor import pandas as pd import os from glob import glob from collections import defaultdict read_params = { 'parse_dates': ['time'], 'dayfirst': True, 'skiprows': 4, 'sep': '\t', 'decimal': ',' } def read_single_file(fn): return pd.read_csv(fn, **read_params).assign(Path=fn) # 先完成文件分组 path = "你的文件路径" filenames = glob(os.path.join(path, "*.xls")) dd = defaultdict(list) for fn in filenames: dd[fn.split('_202')[0]].append(fn) # 并行处理每个分组内的文件 dict_df = {} with ProcessPoolExecutor() as executor: for k, v in dd.items(): df_list = list(executor.map(read_single_file, v)) dict_df[k] = pd.concat(df_list, ignore_index=True)
注意:Windows系统下,ProcessPoolExecutor需要把代码放在
if __name__ == '__main__':块内运行。
4. 只读取需要的列
如果不需要所有列,用usecols指定目标列,减少内存占用和读取时间:
read_params = { # 其他参数不变 'usecols': ['time', 'value', 'id'] # 仅读取所需列 }
5. 纠正append的误区
你听说的"append更快"是错误的——pandas的DataFrame.append()效率极低,每次调用都会创建新的DataFrame对象,反而不如收集所有DF到列表后一次性pd.concat高效,这也是你最初代码的核心思路,只是可通过上述方法进一步优化。
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

