category类型通过整数编码数组+去重字符串列表替代重复object字符串,100万行50个唯一城市从320mb降至18mb;唯一率超5%时转category反而更占内存;含nan或categories不一致会导致merge、groupby等静默出错。

因为 category 类型用整数编码数组 + 一份去重字符串列表替代了重复存储的原始字符串对象,不是压缩,而是去重+索引。
为什么字符串列内存爆炸?
Pandas 默认把字符串存为 object 类型,每行都是一个独立 Python 字符串对象——带引用计数、哈希缓存、内存碎片。哪怕 100 万行全是 "北京",它也存 100 万次完整副本。
- 每个
object字符串实际开销远超字符本身(比如 "北京" 占 48 字节以上) -
category把这 100 万次替换成:一个长度为 100 万的int8数组(1 字节 × 100 万 = 1MB)+ 一个含唯一值的短列表(如["北京", "上海", "广州"],几百字节) - 实测:100 万行、50 个唯一城市的列,从 320MB → 18MB
什么时候转 category 反而更占内存?
当唯一值太多时,categories 列表本身就会存下几乎全部原始字符串,再加一层整数索引,总内存比原 object 还高。
- 判断依据:
df["col"].nunique() / len(df) > 0.05(唯一率超 5%) - 典型反例:用户 ID、订单号、长 URL、随机哈希值、地址详情字段
- 别凭感觉转,先跑
df["col"].nunique()和len(df)
直接 astype('category') 会踩哪些坑?
它看似一行解决,但静默引入三类问题:
- 含
NaN时,NaN被当作合法 category,导致value_counts(dropna=False)多出一行,groupby结果偏移 - 多个 DataFrame 分别调用,
categories顺序/内容不一致,merge或concat会静默失败(值相同但编码不同) - 反复调用会重建
categories,白耗 CPU;推荐显式构造:pd.Categorical(df["col"], categories=df["col"].dropna().unique(), ordered=False)
转完 category,哪些操作会悄悄变慢甚至崩溃?
省内存 ≠ 全面提速。有些操作会隐式退回到 object,触发完整拷贝:
-
.str方法(如.str.contains())会触发拷贝,内存瞬间暴涨 -
sort_values()默认按categories定义顺序排,不是字典序;若列是时间字符串如"2023-01"/"2023-10",得显式设ordered=True或改用pd.Categorical(..., ordered=True) -
query("col in ['A','B']")会隐式转换,应改用df["col"].isin(["A","B"])
最容易被忽略的是索引:如果 df.index.dtype == 'object'(比如 UUID 或业务 ID 做索引),它一样吃内存,检查方式是 df.index.memory_usage(deep=True),必要时重置或转为列再处理。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











