两个文件夹路径中文件转换规整的Python解决案例

案例目的

有效处理产品简称和币种中不包含下划线的情况,确保文件被正确重命名和复制到目标目录。

案例流程

  1. 配置参数:
    • SOURCE_ROOT为包含情景文件夹的根目录路径
    • TARGET_ROOT为文件要保存的目标目录路径
  2. 文件处理流程:
    • 自动识别符合产品简称_币种格式的文件夹(如FMT_CNY)
    • 在文件夹根目录查找测试集文件(模式:*STEAM_文件夹名.csv)
    • 在VAL子目录查找验证集文件(模式:*_STEAM_文件夹名_VALIDATION.csv)
    • 按指定格式重命名并复制到目标目录
  3. 重命名规则:
    • 测试集:TERM_PM_{币种}TEST{产品简称}.csv
      · 验证集:TERM_PM_{币种}VAL{产品简称}.csv
  4. 示例转换:
    • 原始测试集:/FMT_CNY/20241231_STEAM_FMT_CNY_.csv
    • 新文件名:TERM_PM_CNY_TEST_FMT.csv
    • 原始验证集:/FMT_CNY/VAL/20241231_STEAM_FMT_CNY_VALIDATION.csv
    • 新文件名:TERM_PM_CNY_VAL_FMT.csv

案例重点

  1. 精确匹配:
    • 使用split(‘_’, 1)确保只分割一次下划线
  2. 最新文件选择:
    • 通过文件名排序选择最新文件(文件名开头是日期格式)
    • 自动跳过旧版本文件
  3. 健壮性设计:
    • 自动创建目标目录(如果不存在)
    • 跳过不符合命名规则的文件夹
    • 处理VAL目录不存在的情况
  4. 保留元数据:
    • 使用shutil.copy2保留文件原始元数据和时间戳
import os
import glob
import shutil
import re

def process_data_folders(source_root, target_root):
    """
    处理数据情景文件夹中的文件:
    1. 识别产品简称+币种命名的文件夹
    2. 查找测试集和验证集文件
    3. 重命名并复制到目标目录
    """
    # 确保目标目录存在
    os.makedirs(target_root, exist_ok=True)
    
    # 遍历根目录下的所有文件夹
    for folder_name in os.listdir(source_root):
        folder_path = os.path.join(source_root, folder_name)
        
        # 只处理文件夹
        if not os.path.isdir(folder_path):
            continue
            
        # 检查文件夹名称格式 (产品简称_币种)
        if '_' not in folder_name:
            continue
            
        # 分割产品简称和币种 (确保只分割一次)
        parts = folder_name.split('_', 1)
        if len(parts) != 2:
            continue
            
        product, currency = parts
        
        # 处理测试集文件 (位于情景文件夹根目录)
        test_pattern = f"*_STEAM_{folder_name}_.csv"
        test_files = glob.glob(os.path.join(folder_path, test_pattern))
        
        if test_files:
            # 选择最新的测试集文件 (按文件名排序)
            latest_test = sorted(test_files, key=os.path.basename)[-1]
            # 构建新文件名
            new_test_name = f"TERM_PM_{currency}_TEST_{product}.csv"
            shutil.copy2(latest_test, os.path.join(target_root, new_test_name))
        
        # 处理验证集文件 (位于VAL子文件夹)
        val_dir = os.path.join(folder_path, "VAL")
        if os.path.exists(val_dir):
            val_pattern = f"*_STEAM_{folder_name}_VALIDATION.csv"
            val_files = glob.glob(os.path.join(val_dir, val_pattern))
            
            if val_files:
                # 选择最新的验证集文件
                latest_val = sorted(val_files, key=os.path.basename)[-1]
                new_val_name = f"TERM_PM_{currency}_VAL_{product}.csv"
                shutil.copy2(latest_val, os.path.join(target_root, new_val_name))

def main():
    # 配置路径
    SOURCE_ROOT = "/path/to/your/source/root"  # 替换为您的源目录
    TARGET_ROOT = "/path/to/your/target/root"  # 替换为目标目录
    
    process_data_folders(SOURCE_ROOT, TARGET_ROOT)
    print("文件处理完成!")

if __name__ == "__main__":
    main()

使用步骤

  1. 将脚本中的SOURCE_ROOT和TARGET_ROOT替换为实际路径
  2. 运行脚本
  3. 检查目标目录中的文件是否符合命名规范

注意事项

  1. 如果同一情景文件夹中有多个测试集/验证集文件,脚本会选择文件名最大的(通常是最新的)文件
  2. 脚本不会删除原始文件,只会在目标目录创建副本
  3. 如果产品简称或币种包含下划线,该文件夹会被跳过
  4. 如果不需要保留元数据,可将shutil.copy2改为shutil.copy

V2.0

使用 concurrent.futures 优化,利用多线程批量文件操作。

import os
import glob
import shutil
import re
import pandas as pd
from concurrent.futures import ThreadPoolExecutor, as_completed

def move_column_efficient(source_file, target_file, col_identifier, new_idx):
    """
    高效处理CSV文件:读取、处理列、保存
    """
    # 读取CSV文件
    df = pd.read_csv(source_file)
    
    # 这里添加数据处理逻辑
    # 例如:移动列、重命名列、过滤数据等
    
    # 保存处理后的数据
    df.to_csv(target_file, index=False)
    return target_file

def process_single_folder(folder_path, target_root, folder_name):
    """
    处理单个文件夹中的文件
    """
    # 分割产品简称和币种 (确保只分割一次)
    parts = folder_name.split('_', 1)
    if len(parts) != 2:
        return []
    
    product, currency = parts
    processed_files = []
    
    # 处理测试集文件 (位于情景文件夹根目录)
    test_pattern = f"*_STEAM_{folder_name}_.csv"
    test_files = glob.glob(os.path.join(folder_path, test_pattern))
    
    if test_files:
        # 选择最新的测试集文件 (按文件名排序)
        latest_test = sorted(test_files, key=os.path.basename)[-1]
        # 构建新文件名
        new_test_name = f"TERM_PM_{currency}_TEST_{product}.csv"
        target_path = os.path.join(target_root, new_test_name)
        move_column_efficient(latest_test, target_path, col_identifier, new_idx)
        processed_files.append(target_path)
    
    # 处理验证集文件 (位于VAL子文件夹)
    val_dir = os.path.join(folder_path, "VAL")
    if os.path.exists(val_dir):
        val_pattern = f"*_STEAM_{folder_name}_VALIDATION.csv"
        val_files = glob.glob(os.path.join(val_dir, val_pattern))
        
        if val_files:
            # 选择最新的验证集文件
            latest_val = sorted(val_files, key=os.path.basename)[-1]
            new_val_name = f"TERM_PM_{currency}_VAL_{product}.csv"
            target_path = os.path.join(target_root, new_val_name)
            move_column_efficient(latest_val, target_path, col_identifier, new_idx)
            processed_files.append(target_path)
    
    return processed_files

def process_data_folders_parallel(source_root, target_root, max_workers=None):
    """
    并行处理数据情景文件夹中的文件
    """
    # 确保目标目录存在
    os.makedirs(target_root, exist_ok=True)
    
    # 获取所有文件夹
    folders = []
    for folder_name in os.listdir(source_root):
        folder_path = os.path.join(source_root, folder_name)
        if os.path.isdir(folder_path) and '_' in folder_name:
            folders.append((folder_path, target_root, folder_name))
    
    # 使用线程池并行处理
    processed_files = []
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        # 提交所有任务
        future_to_folder = {
            executor.submit(process_single_folder, *args): args 
            for args in folders
        }
        
        # 收集结果
        for future in as_completed(future_to_folder):
            try:
                result = future.result()
                processed_files.extend(result)
            except Exception as exc:
                folder_args = future_to_folder[future]
                print(f"处理文件夹 {folder_args[2]} 时生成异常: {exc}")
    
    return processed_files

def main():
    # 配置路径
    SOURCE_ROOT = "/path/to/your/source/root"  # 替换为您的源目录
    TARGET_ROOT = "/path/to/your/target/root"  # 替换为目标目录
    
    # 设置最大工作线程数 (None表示使用默认值,通常为CPU核心数*5)
    # 对于I/O密集型任务,可以设置更高,如16或32
    MAX_WORKERS = 16
    
    # 并行处理文件夹
    processed_files = process_data_folders_parallel(SOURCE_ROOT, TARGET_ROOT, MAX_WORKERS)
    
    print(f"处理完成!共处理了 {len(processed_files)} 个文件")

if __name__ == "__main__":
    main()

优化说明

  1. 并行处理结构:

    • 将原来的顺序处理改为并行处理,每个文件夹的处理作为一个独立任务
    • 使用 ThreadPoolExecutor 管理线程池
  2. 函数重构:

    • process_single_folder: 处理单个文件夹的逻辑
    • process_data_folders_parallel: 并行调度所有文件夹处理
  3. 参数说明:

    • max_workers: 控制并行线程数
      • 对于I/O密集型任务,可以设置较高值(如16-32)
      • 如果遇到磁盘I/O瓶颈,可以适当降低此值
  4. 异常处理:

    • 使用 as_completed 捕获和处理单个文件夹处理中的异常
    • 不会因为一个文件夹处理失败而影响其他文件夹
  5. 内存考虑:

    • 内存足够大(128GB),可以同时处理多个文件
    • 如果遇到内存问题,可以降低 max_workers 或使用批处理

性能优化建议

  1. 调整线程数:

    • 开始时可以尝试 max_workers=8,然后逐步增加观察效果
    • 使用 time 命令测量执行时间:time python your_script.py
  2. 监控资源使用:

    • 使用 htop 或任务管理器监控CPU和内存使用情况
    • 如果磁盘I/O成为瓶颈,考虑减少线程数或使用SSD
  3. 进一步优化:

    • 如果 move_column_efficient 中的数据处理很耗时,可以考虑优化这些操作
    • 对于特别大的CSV文件,可以考虑使用 dask 或分块处理

V2.1

import os
import glob
import re
import pandas as pd
from concurrent.futures import ProcessPoolExecutor, as_completed
import multiprocessing
from functools import partial

# 定义全局变量(如果需要)
col_identifier = 'CC'  # 示例值,请根据实际情况修改
new_idx = 1  # 示例值,请根据实际情况修改

def move_column_efficient(source_file, target_file, col_identifier, new_idx):
    """
    高效处理CSV文件:读取、处理列、保存
    """
    try:
        # 读取CSV文件
        df = pd.read_csv(source_file)
        
        # 这里添加数据处理逻辑
        # 例如:移动列、重命名列、过滤数据等
        
        # 保存处理后的数据
        df.to_csv(target_file, index=False)
        return target_file
    except Exception as e:
        print(f"处理文件 {source_file} 时出错: {str(e)}")
        return None

def process_single_folder(folder_info):
    """
    处理单个文件夹中的文件
    """
    folder_path, target_root, folder_name = folder_info
    
    try:
        # 分割产品简称和币种 (确保只分割一次)
        parts = folder_name.split('_', 1)
        if len(parts) != 2:
            return []
        
        product, currency = parts
        processed_files = []
        
        # 处理测试集文件 (位于情景文件夹根目录)
        test_pattern = f"*_STEAM_{folder_name}_.csv"
        test_files = glob.glob(os.path.join(folder_path, test_pattern))
        
        if test_files:
            # 选择最新的测试集文件 (按文件名排序)
            latest_test = sorted(test_files, key=os.path.basename)[-1]
            # 构建新文件名
            new_test_name = f"TERM_PM_{currency}_TEST_{product}.csv"
            target_path = os.path.join(target_root, new_test_name)
            result = move_column_efficient(latest_test, target_path, col_identifier, new_idx)
            if result:
                processed_files.append(result)
        
        # 处理验证集文件 (位于VAL子文件夹)
        val_dir = os.path.join(folder_path, "VAL")
        if os.path.exists(val_dir):
            val_pattern = f"*_STEAM_{folder_name}_VALIDATION.csv"
            val_files = glob.glob(os.path.join(val_dir, val_pattern))
            
            if val_files:
                # 选择最新的验证集文件
                latest_val = sorted(val_files, key=os.path.basename)[-1]
                new_val_name = f"TERM_PM_{currency}_VAL_{product}.csv"
                target_path = os.path.join(target_root, new_val_name)
                result = move_column_efficient(latest_val, target_path, col_identifier, new_idx)
                if result:
                    processed_files.append(target_path)
        
        return processed_files
    except Exception as e:
        print(f"处理文件夹 {folder_name} 时出错: {str(e)}")
        return []

def process_data_folders_parallel(source_root, target_root, max_workers=None):
    """
    并行处理数据情景文件夹中的文件
    """
    # 确保目标目录存在
    os.makedirs(target_root, exist_ok=True)
    
    # 获取所有文件夹
    folders = []
    for folder_name in os.listdir(source_root):
        folder_path = os.path.join(source_root, folder_name)
        if os.path.isdir(folder_path) and '_' in folder_name:
            folders.append((folder_path, target_root, folder_name))
    
    # 设置合理的最大工作进程数
    if max_workers is None:
        max_workers = min(len(folders), multiprocessing.cpu_count())
    
    processed_files = []
    
    # 使用进程池并行处理
    with ProcessPoolExecutor(max_workers=max_workers) as executor:
        # 提交所有任务
        future_to_folder = {
            executor.submit(process_single_folder, folder_info): folder_info 
            for folder_info in folders
        }
        
        # 收集结果
        for future in as_completed(future_to_folder):
            try:
                result = future.result(timeout=3600)  # 设置超时时间(1小时)
                processed_files.extend(result)
            except Exception as exc:
                folder_info = future_to_folder[future]
                print(f"处理文件夹 {folder_info[2]} 时生成异常: {exc}")
    
    return processed_files

def main():
    # 配置路径
    SOURCE_ROOT = "/path/to/your/source/root"  # 替换为您的源目录
    TARGET_ROOT = "/path/to/your/target/root"  # 替换为目标目录
    
    # 设置最大工作进程数
    MAX_WORKERS = multiprocessing.cpu_count()  # 使用CPU核心数
    
    print(f"开始处理,使用 {MAX_WORKERS} 个进程")
    
    # 并行处理文件夹
    processed_files = process_data_folders_parallel(SOURCE_ROOT, TARGET_ROOT, MAX_WORKERS)
    
    print(f"处理完成!共处理了 {len(processed_files)} 个文件")

if __name__ == "__main__":
    # 在Windows上使用多进程时必须保护主模块
    multiprocessing.freeze_support()  # 可选,对于打包成exe有用
    main()

关键修改和说明

  1. 参数传递

    • 确实需要将参数打包成元组传递,因为多进程需要序列化参数
    • 修改了 process_single_folder 函数以接受单个元组参数
  2. 异常处理

    • 在关键位置添加了异常捕获,防止单个任务失败导致整个进程崩溃
    • 添加了超时设置,防止任务无限期挂起
  3. 进程数设置

    • 使用 multiprocessing.cpu_count() 获取CPU核心数
    • 设置合理的最大进程数,避免创建过多进程导致系统资源耗尽
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值