category类型能大幅降低内存占用,因其用整数编码数组+共享字符串数组替代重复字符串引用,对低基数列压缩率达5–10倍;高基数列则可能增耗内存。

为什么 category 类型能大幅降低内存占用?
字符串列或低基数(low-cardinality)离散列(比如“省份”“状态”“产品类别”)用 object 类型存储时,Pandas 为每个单元格单独存一份字符串对象引用,重复值越多,浪费越明显。改用 category 后,底层只存一个共享的字符串数组(categories)+ 一个整数编码数组(codes),内存常可压缩 5–10 倍。
但不是所有列都适合:高基数列(如用户 ID、时间戳字符串)转 category 反而可能更占内存,因为 categories 数组本身会膨胀。
实操建议:
- 先用
df.nunique() / len(df)粗筛:比值 category - 对
object列执行df[col].nunique(),确认实际唯一值数量是否远小于行数 - 避免对含大量
NaN的列直接转换——NaN在category中仍占位,且部分聚合操作(如groupby().size())行为会变
如何安全批量转换多列并保留原始顺序?
用 astype('category') 单列转换没问题,但批量处理时容易忽略 categories 的顺序一致性。比如两列都含 ['A', 'B', 'C'],但一列内部排序是 ['B','A','C'],另一列是 ['A','C','B'],后续做 pd.concat() 或 merge 就可能出错。
立即学习“Python免费学习笔记(深入)”;
推荐做法是显式控制 categories 顺序:
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
- 对每列先用
sorted(df[col].dropna().unique())获取统一排序的唯一值列表 - 再用
pd.Categorical(df[col], categories=ordered_cats, ordered=False)构造,然后赋回 - 或者更简洁:用
df[col].astype(pd.CategoricalDtype(categories=ordered_cats)),它能自动对齐缺失值处理逻辑
示例:
cats = sorted(df['status'].dropna().unique()) df['status'] = df['status'].astype(pd.CategoricalDtype(categories=cats))
category 转换后哪些操作会变慢或失效?
转 category 不是纯内存优化,它改变了底层数据结构,有些操作代价会上升:
-
df[col].str.contains(...)会报AttributeError——category列没有.str访问器,必须先转回object:df[col].astype(str).str.contains(...) -
df[col].apply(func)若func依赖原始字符串行为(比如正则分组名),可能因category的__repr__或索引机制出错 - 用
query()过滤时,写df.query("col == 'A'")没问题,但df.query("col in @my_list")中若my_list是普通字符串列表,Pandas 会隐式转换,小数据无感,大数据可能触发全量解码
关键点:别在热路径上频繁做 astype(str) 或 .values 解包,这会抵消掉内存优势。
如何验证瘦身效果并防止意外退化?
转换前后必须对比 df.memory_usage(deep=True).sum(),而不是只看 df.info() 的粗略估算。尤其注意 deep=True —— 它会递归计算 object 列里字符串的实际字节占用。
容易被忽略的退化场景:
- 读取 CSV 时用了
dtype={'col': 'category'},但文件里该列有新值未出现在首块 chunk 中 → 后续 chunk 里的新值会被设为NaN,categories 不全,内存没省多少还丢了数据 - 用
pd.concat([df1, df2])合并两个含category列的 DataFrame 时,若两者的categories不一致,结果列会自动退化为object——df.dtypes一眼就能发现,但容易漏看 - 保存为 Parquet 时默认不保留 categories 顺序,下次读入可能乱序;应显式设
use_dictionary=True并检查read_parquet(...).dtypes
真正省内存,靠的是全程可控:从加载、转换、计算到落盘,每一步都要确认 dtype 和 memory_usage 行为符合预期。

















