You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python处理百万行CSV速度过慢,求高效优化方案

高效处理大CSV文件修正SPECIAL_ID字段的优化方案

你的问题核心是处理超大规模CSV时的性能瓶颈,原来的两种方案都因为大量Python层面的循环和低效操作(比如DataFrame.append()、多次筛选子DataFrame)导致速度极慢。下面给你两个层级的优化方案,从快速解决当前问题到适配未来1-2GB级别的超大文件:

一、当前200万行文件的最优快速方案(基于Pandas向量化操作)

Pandas的groupby + cumcount()是专门解决分组内递增计数的向量化工具,完全避免Python循环,性能提升至少一个数量级。

核心思路:

  1. 按day, month, year分组,用cumcount()生成每个分组内的递增序号(从0开始),加1后格式化为三位补零的字符串。
  2. 提取SPECIAL_ID的前缀(__-之前的部分),拼接上生成的三位序号,替换原字段。
  3. 直接写入CSV,全程避免逐行操作和低效的DataFrame拼接。

代码实现:

import pandas as pd

# 读取文件(内存吃紧可加low_memory=False)
df = pd.read_csv(filename, delimiter=';')

# 1. 按日期分组生成递增序号,格式化为三位补零
df['seq'] = df.groupby(['day', 'month', 'year']).cumcount() + 1
df['seq_str'] = df['seq'].apply(lambda x: f"{x:03d}")

# 2. 替换SPECIAL_ID:提取前缀 + 拼接序号
df['SPECIAL_ID'] = df['SPECIAL_ID'].str.replace('__-', '') + df['seq_str']

# 3. 保存结果(删除临时列,或直接指定要保存的列)
df.drop(columns=['seq', 'seq_str']).to_csv(filename_fixed, sep=';', encoding='utf-8', index=False)

为什么这个方案快?

  • groupby.cumcount()是Pandas底层实现的向量化操作,用C语言循环,比Python的iterrows()快几十倍。
  • 全程没有逐行修改和DataFrame拼接,避免了append()带来的O(n²)时间复杂度。

二、适配未来1-2GB超大文件的分块处理方案

如果文件大到内存无法一次性加载,可以用分块读取+全局计数的方式,保证跨块的日期分组序号连续:

核心步骤:

  1. 先遍历所有块,统计每个日期分组的总数量,记录每个分组的起始序号。
  2. 再次分块读取,对每个块内的日期分组,计算当前块内的序号范围(起始序号到起始序号+块内数量-1),生成序号后替换SPECIAL_ID。
  3. 逐块追加写入CSV,避免内存溢出。

代码实现:

import pandas as pd
from collections import defaultdict

filename = "your_large_file.csv"
filename_fixed = "fixed_large_file.csv"
chunksize = 100000  # 按10万行一块调整,根据内存情况修改

# 第一步:全局统计每个日期分组的总数量
date_counts = defaultdict(int)
for chunk in pd.read_csv(filename, delimiter=';', chunksize=chunksize):
    chunk_counts = chunk.groupby(['day', 'month', 'year']).size()
    for (d, m, y), cnt in chunk_counts.items():
        date_counts[(d, m, y)] += cnt

# 计算每个日期分组的起始序号(前一个分组的累计和)
current_start = 1
date_start = {}
for key in sorted(date_counts.keys()):
    date_start[key] = current_start
    current_start += date_counts[key]

# 第二步:分块处理并写入
first_chunk = True
for chunk in pd.read_csv(filename, delimiter=';', chunksize=chunksize):
    # 给当前块的每个行生成序号
    chunk['seq'] = chunk.groupby(['day', 'month', 'year']).cumcount() + 1
    chunk['start_seq'] = chunk.apply(lambda row: date_start[(row['day'], row['month'], row['year'])], axis=1)
    chunk['final_seq'] = chunk['start_seq'] + chunk['seq'] - 1
    chunk['seq_str'] = chunk['final_seq'].apply(lambda x: f"{x:03d}")
    
    # 替换SPECIAL_ID
    chunk['SPECIAL_ID'] = chunk['SPECIAL_ID'].str.replace('__-', '') + chunk['seq_str']
    
    # 写入文件,第一块写表头,后续块追加
    chunk.drop(columns=['seq', 'start_seq', 'final_seq', 'seq_str']).to_csv(
        filename_fixed,
        sep=';',
        encoding='utf-8',
        index=False,
        mode='w' if first_chunk else 'a',
        header=first_chunk
    )
    first_chunk = False

注意事项:

  • chunksize的大小根据你的内存情况调整,比如8GB内存可以设为20万行。
  • 如果日期分组的顺序不影响序号连续性,可以不用排序,直接累计计数。

验证结果

用你给出的示例数据测试,上述方案会生成完全符合预期的输出:

原行8的SPECIAL_ID会从21102011505__-变为21102011505002,和你预期的一致。

内容的提问来源于stack exchange,提问作者Kristjan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:24:03