优先用usecols显式选列、设最小dtype、禁用low_memory、分批处理describe/dtypes、concat前统一列名并禁用对齐、用布尔索引替代dropna,避免pandas宽表场景的非线性开销。

宽表读取时内存爆炸,read_csv卡死或OOM怎么办?
Pandas 默认按列加载全部数据到内存,上万列的 CSV 文件即使每列只存几个字节,也会因 DataFrame 的内部结构(如 dtype 推断、索引开销、块对齐)导致内存占用翻 3–5 倍。直接调用 read_csv 很可能触发 MemoryError 或系统 swap。
- 优先用
usecols显式指定需要的列,哪怕只用 10 列也别留空 - 设置
dtype为最小可行类型:比如用'category'替代object,用np.int8替代默认int64 - 关闭自动类型推断:
low_memory=False反而更稳(避免分块重推 dtype 导致重复解析) - 若只需统计或采样,加
nrows=1000快速探查结构,再决定后续策略
df = pd.read_csv("wide.csv",
usecols=range(0, 50), # 只读前 50 列
dtype={i: 'category' for i in range(0, 20)} |
{i: 'float32' for i in range(20, 50)})
列太多导致 df.describe() 或 df.dtypes 极慢
这不是计算慢,而是 Pandas 在生成描述性统计或 dtype 摘要时,会遍历所有列做类型检查和空值扫描——上万列下光是 df.dtypes 就可能耗时数分钟。
- 避免全量调用
describe();改用df[subset_cols].describe()分批处理 - 查 dtype 不必
df.dtypes:用df._mgr.blocks(内部属性,不保证稳定但极快)或直接df.iloc[:, :100].dtypes抽样 - 若需全量类型统计,用
df.memory_usage(deep=True).sum()配合df.dtypes.value_counts()(后者仍慢,但比全量dtypes少一次遍历)
用 pd.concat 拼接多个宽表时 CPU 占满、卡住
宽表 concat 的瓶颈不在数据合并本身,而在 Pandas 对齐列名时的 O(n²) 字符串匹配——上万列意味着数千万次列名哈希与比较。
- 确保所有待拼接 DataFrame 的列顺序完全一致,且列名是
tuple或str(避免混合类型) - 关闭列对齐:
pd.concat([df1, df2], ignore_index=True, join='inner'),并提前用df.reindex(columns=common_cols)统一列集 - 更彻底的解法:改用
pyarrow.Table拼接后再转 pandas,Arrow 的列式拼接无列名匹配开销
# 不推荐(隐式列对齐) pd.concat([df_a, df_b]) <h1>推荐(显式对齐 + 禁用自动匹配)</h1><p>common_cols = list(set(df_a.columns) & set(df_b.columns)) pd.concat([df_a[common_cols], df_b[common_cols]], ignore_index=True, join='outer')</p>
想删掉大量空列,df.dropna(how='all', axis=1) 太慢
dropna 对每列逐个扫描非空值,上万列就是上万次 C-level 循环 + Python 层回调,I/O 和分支预测开销极大。
- 改用布尔索引:
df.loc[:, df.notna().any()],底层走 NumPy 向量化,快 5–10 倍 - 如果空列全是
NaN或None,直接用df.columns[df.isnull().all()]找出再删 - 对超宽表,考虑先用
df.iloc[:, ::100]采样检查空列分布,再批量操作
真正卡住的地方往往不是“怎么写”,而是没意识到 Pandas 的很多方法在宽表场景下根本不是线性复杂度——它默认为「行多列少」优化,遇到上万列就暴露底层设计假设。动手前先确认:你真的需要把全部列一次性装进 DataFrame 吗?
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











