字符串列占用内存高因pandas默认存为object类型,每个值都是独立python字符串对象;改用category类型可大幅降低内存,如100万行从320mb降至18mb。

为什么字符串列吃掉你80%的内存
因为Pandas默认把字符串存成object类型,每个值都是独立的Python字符串对象——带引用计数、哈希缓存、内存碎片。哪怕一列只有10个重复城市名,它也存10万次完整字符串副本。
用category类型不是“锦上添花”,是直接砍掉冗余引用和重复内容。实测:100万行含50个唯一字符串的列,从320MB降到18MB。
- 只对低基数(unique值数量 / 总行数 category反而更占内存
-
category本质是两个数组:一个整数编码数组(int8/int16等),一个去重后的字符串列表(categories) - 一旦设为
category,新增未见过的值会报ValueError: Cannot setitem on a Categorical with a new category
怎么安全地把字符串列转成category
别直接df['col'] = df['col'].astype('category')——如果列里有NaN,它会悄悄把NaN当做一个合法category,后续groupby或value_counts可能出偏。
正确做法是显式控制缺失值行为:
- 先检查唯一值数量:
df['col'].nunique()和len(df)对比,确认是否适合转 - 用
pd.Categorical构造时加ordered=False(除非真需要排序语义) - 明确处理
NaN:df['col'] = pd.Categorical(df['col'], categories=df['col'].dropna().unique()) - 或者更稳妥:
df['col'] = df['col'].astype('category').cat.remove_unused_categories()
category类型在merge和filter时的坑
两表按category列merge,如果它们的categories顺序或内容不一致,结果会静默出错——比如某值在左表category索引是2,在右表是5,Pandas不会报错,但匹配失败。
- merge前务必统一category:
common_cats = sorted(set(left['col'].cat.categories) | set(right['col'].cat.categories)),再分别用.cat.set_categories(common_cats) -
query()中写"col == 'Beijing'"没问题,但"col in ['Beijing', 'Shanghai']"会触发隐式转换,慢且可能漏数据;改用df['col'].isin([...]) - 用
category列做groupby通常更快,但如果分组数极多(比如>1000),底层排序开销可能反超object
内存没降下来?检查这几个地方
转了category但df.memory_usage(deep=True).sum()几乎不变,大概率卡在别处:
- 其他列还是
object——逐列检查:df.dtypes,重点盯object类型列 - 索引是字符串型:
df.index.dtype == 'object'?用df.reset_index(drop=True)或df.index = pd.RangeIndex(len(df))释放 - 用了
copy()或链式赋值产生中间对象,内存没及时回收;加del df_old+gc.collect()观察 - 某些字符串列其实含大量空格或大小写不一致,导致
nunique()虚高;先.str.strip().str.lower()再转
category不是银弹,但它是最容易见效的内存优化动作之一。真正麻烦的是那些混着数字、布尔、嵌套JSON的object列——那种得拆、得正则、得重构,category只是第一步。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











