我们用最通俗易懂的方式,向初学者讲清楚:
🎯 Pandas 的 category
数据类型:为什么用?怎么用?如何节省内存?
🧩 一、什么是 category 类型?
Pandas 的 category
是一种专门为"重复文本"设计的数据类型。
👉 想象你有一个"城市"列,里面只有:北京、上海、广州、深圳
,但重复了 100 万行。
- 默认是
object
类型(本质是字符串),每一行都存完整字符串 → 很占内存! - 如果转成
category
,Pandas 只存一次"北京、上海、广州、深圳",然后每行只存一个数字编号(比如 0,1,2,3)→ 内存暴降!
💾 二、为什么 category 能省内存?
🆚 对比:object
vs category
假设我们有这样一列:
python
import pandas as pd
cities = ['北京', '上海', '广州', '深圳'] * 250000 # 总共 100 万行
df = pd.DataFrame({'city': cities})
✅ 查看内存占用:
python
print(df['city'].memory_usage(deep=True)) # 单位:字节
默认 object 类型 → ❌ 占用约 48MB
python
df['city'].dtype # dtype('O') → object
转成 category → ✅ 占用约 1MB
python
df['city'] = df['city'].astype('category')
print(df['city'].memory_usage(deep=True)) # 瞬间降到 1MB 左右!
💡 节省了 98% 的内存!
📊 三、category 是怎么做到的?
它内部用了两个结构:
- categories(字典) :存储所有不重复的值 →
['北京', '上海', '广州', '深圳']
- codes(编码) :每一行存的是这个值在字典中的索引 →
[0, 1, 2, 3, 0, 1, ...]
📌 就像把"北京"压缩成"0","上海"压缩成"1",以此类推。
✅ 四、什么时候该用 category?
记住这个口诀:
🔁 值少、重复多 → 用 category!
典型场景:
- 性别:男 / 女
- 省份、城市、国家
- 产品类别:手机、电脑、家电
- 订单状态:待支付、已发货、已完成
- 星级评分:1星、2星、3星、4星、5星
⚠️ 不适合的场景:
- 每行都是唯一的值(如用户ID、姓名、地址)
- 值非常多(比如 > 50% 不重复),反而可能更占内存!
🛠️ 五、怎么转换成 category?
方法1:创建时指定
python
df = pd.DataFrame({
'city': pd.Categorical(['北京', '上海', '北京', '广州'])
})
方法2:事后转换(最常用)
python
df['city'] = df['city'].astype('category')
方法3:批量转换(适合多列)
python
cat_cols = ['city', 'gender', 'status']
df[cat_cols] = df[cat_cols].astype('category')
🧪 六、实际内存对比演示
我们来跑个真实例子:
python
import pandas as pd
import numpy as np
# 生成 100 万行数据
np.random.seed(42)
cities = np.random.choice(['北京', '上海', '广州', '深圳', '杭州'], size=1000000)
df = pd.DataFrame({'city': cities})
# 查看 object 类型内存
mem_before = df['city'].memory_usage(deep=True)
print(f"object 类型内存: {mem_before / 1024**2:.2f} MB")
# 转成 category
df['city'] = df['city'].astype('category')
# 查看 category 类型内存
mem_after = df['city'].memory_usage(deep=True)
print(f"category 类型内存: {mem_after / 1024**2:.2f} MB")
# 节省比例
print(f"节省了: {100 * (1 - mem_after / mem_before):.1f}%")
✅ 输出示例:
csharp
object 类型内存: 47.68 MB
category 类型内存: 1.00 MB
节省了: 97.9%
🚀 七、额外好处:不止省内存!
除了节省内存,category
还有这些优势:
优势 | 说明 |
---|---|
⚡ 计算更快 | 排序、分组、去重等操作更快(因为内部是数字编码) |
📈 排序可控 | 可以自定义顺序,比如:['低', '中', '高'] ,而不是按字母排序 |
🧮 统计友好 | .value_counts() 更快更准 |
🖥️ 节省磁盘空间 | 保存为 parquet/feather 时体积更小 |
🎨 八、自定义排序(bonus!)
默认 category 是按字母排序,但你可以指定顺序:
python
from pandas.api.types import CategoricalDtype
# 定义有序 category
size_order = CategoricalDtype(['小', '中', '大'], ordered=True)
df['size'] = df['size'].astype(size_order)
# 现在可以正确排序:
df.sort_values('size') # 小 → 中 → 大,而不是 中→大→小(按拼音)
📌 九、初学者常见误区
❌ 误区1:所有文本列都转 category
→ 不!如果每行都不同(如"用户评论"),转 category 会更占内存!
❌ 误区2:转了 category 不能改值
→ 可以改!但新增"没见过的值"会变成 NaN
,需手动添加:
python
df['city'] = df['city'].cat.add_categories(['成都'])
df.loc[0, 'city'] = '成都' # 现在可以了
❌ 误区3:category 是字符串
→ 不是!它是独立类型,不能直接做字符串操作(如 .str.upper()
),需要先 .astype(str)
。
✅ 十、总结:一张表记住 category
项目 | 说明 |
---|---|
适用场景 | 重复文本、枚举值、分类变量 |
核心原理 | 存字典 + 存编号,避免重复存储字符串 |
节省内存 | 重复越多,省得越多(可达 90%+) |
转换方法 | .astype('category') |
额外优势 | 运算快、排序可控、统计高效 |
注意事项 | 不适合唯一值多的列;新增值要手动添加 |
💡 终极建议
下次你看到一列文本数据,先问自己:
"这列有多少种不同的值?" "是不是大量重复?"
如果是 → 立刻
.astype('category')
,内存和性能双提升!
🎯 现在你已经完全掌握 Pandas 的 category
类型了!快去优化你的数据集吧!