如何优化pandas DataFrame的explode操作,降低大文件处理内存占用?
大尺寸CSV多列分隔符拆分的低内存实现方案
原代码内存溢出的核心原因是一次性加载全量数据到内存,拆分+爆炸操作会使数据量成倍放大,叠加pandas的内存开销导致超出硬件限制。以下是三种可落地的优化方案,内存占用均可控制在1G以内:
方案1:pandas分块处理(无需换依赖,兼容原有逻辑)
利用pandas read_csv 的分块读取能力,每次只加载少量数据处理后写入结果,全程不会加载全量数据到内存:
import pandas as pd # 配置项 INPUT_CSV = "your_source.csv" OUTPUT_CSV = "processed_result.csv" SPLIT_COLS = ["id"] # 支持配置多列同时拆分,比如["id", "tag"] SEPARATOR = "|" CHUNKSIZE = 100000 # 可根据内存调整,内存不足可降到50000 first_chunk = True # 分块读取,同时提前指定列数据类型降低内存占用 for chunk in pd.read_csv( INPUT_CSV, chunksize=CHUNKSIZE, dtype={ "uid": "int32", "tag": "category", "date": "category" } ): # 拆分所有需要处理的列 for col in SPLIT_COLS: chunk[col] = chunk[col].str.split(SEPARATOR) # 同时爆炸所有拆分列,避免多次操作产生冗余数据 chunk = chunk.explode(SPLIT_COLS) # 去重,可指定subset参数按主键去重,比全列判断更省资源 chunk = chunk.drop_duplicates(subset=["id", "uid", "date"]) # 追加写入结果文件 chunk.to_csv(OUTPUT_CSV, mode="a", header=first_chunk, index=False) first_chunk = False
方案2:逐行流式处理(内存占用最低,<100M)
用Python内置csv模块逐行处理,完全不需要加载全表到内存,适合极端内存受限场景:
import csv from itertools import product # 配置项 INPUT_CSV = "your_source.csv" OUTPUT_CSV = "processed_result.csv" SPLIT_COLS = ["id"] SEPARATOR = "|" FILE_DELIMITER = "\t" # 你的CSV如果是逗号分隔就改成"," NEED_DEDUPLICATE = True seen = set() with open(INPUT_CSV, "r", encoding="utf-8") as f_in, \ open(OUTPUT_CSV, "w", encoding="utf-8", newline="") as f_out: reader = csv.DictReader(f_in, delimiter=FILE_DELIMITER) writer = csv.DictWriter(f_out, fieldnames=reader.fieldnames) writer.writeheader() for row in reader: # 拆分所有指定列 split_values = [row[col].split(SEPARATOR) for col in SPLIT_COLS] # 生成多列拆分后的所有组合 for vals in product(*split_values): new_row = row.copy() for idx, col in enumerate(SPLIT_COLS): new_row[col] = vals[idx] # 去重逻辑,不需要可以直接删除 if NEED_DEDUPLICATE: row_key = tuple(new_row.values()) if row_key in seen: continue seen.add(row_key) writer.writerow(new_row)
方案3:Polars处理(性能最高,速度是pandas的5~10倍)
Polars是Rust实现的高性能数据处理库,默认采用内存映射+懒执行,处理1G级文件完全不需要加载全量数据到内存,代码更简洁:
首先安装依赖:pip install polars
import polars as pl # 配置项 INPUT_CSV = "your_source.csv" OUTPUT_CSV = "processed_result.csv" SPLIT_COLS = ["id"] SEPARATOR = "|" FILE_DELIMITER = "\t" # 懒加载CSV,不占用内存 df = pl.scan_csv(INPUT_CSV, separator=FILE_DELIMITER) # 拆分+爆炸多列 for col in SPLIT_COLS: df = df.with_columns(pl.col(col).str.split(SEPARATOR)).explode(col) # 去重,可指定subset参数按主键去重 df = df.unique(subset=["id", "uid", "date"]) # 执行计算并写入结果 df.write_csv(OUTPUT_CSV)
内容的提问来源于stack exchange,提问作者marlon
相关产品推荐
相关产品推荐

