category 类型不仅能大幅节省内存,还能提高某些操作(如 groupby、排序)的性能,同时支持自定义顺序,方便统计分析和可视化。
Pandas 中可以通过多种方式将列转换为 category 类型:
astype()df['性别'] = df['性别'].astype('category')
pd.Categorical()cat = pd.Categorical(['a','b','a'], categories=['a','b','c'], ordered=False)
pd.CategoricalDtype()dtype = pd.CategoricalDtype(categories=['低','中','高'], ordered=True)
df['等级'] = df['等级'].astype(dtype)
import pandas as pd
import numpy as np
# 创建示例数据
df = pd.DataFrame({
'姓名': ['张三', '李四', '王五', '赵六', '陈七'],
'部门': ['技术', '销售', '技术', '管理', '销售'],
'绩效': ['B', 'A', 'C', 'B', 'A']
})
print(df.dtypes)
# 转换为分类类型
df['部门'] = df['部门'].astype('category')
df['绩效'] = pd.Categorical(df['绩效'], categories=['A','B','C'], ordered=True)
print(df.dtypes)
print(df[['部门', '绩效']].head())
通过 .cat 访问器可以获取分类相关的属性和方法。
# 查看类别
print(df['部门'].cat.categories)
# 查看编码后的整数(每个类别对应的数字)
print(df['部门'].cat.codes)
# 是否有序
print(df['绩效'].cat.ordered) # True
# 查看所有类别(即使数据中没有出现)
print(df['绩效'].cat.categories)
使用 rename_categories() 可以批量重命名类别。
# 将部门英文改为中文(演示)
df['部门'] = df['部门'].cat.rename_categories({'技术': 'Engineering', '销售': 'Sales', '管理': 'Management'})
print(df['部门'].cat.categories)
# 传入列表(长度必须等于原类别数)
df['绩效'] = df['绩效'].cat.rename_categories(['优秀', '良好', '合格'])
print(df['绩效'].cat.categories)
如果新数据中出现未定义的类别,可以通过 add_categories() 或 set_categories() 处理。
# 添加新类别
df['部门'] = df['部门'].cat.add_categories(['HR', 'Finance'])
print(df['部门'].cat.categories)
# 删除类别(注意:如果数据中有该类别,会变成 NaN)
df['部门'] = df['部门'].cat.remove_categories(['Finance']) # 假设没有数据
# 直接设置新类别(可以同时增删,不在新类别中的原值变为 NaN)
df['部门'] = df['部门'].cat.set_categories(['Engineering', 'Sales', 'HR'])
有序分类可以比较大小、排序。
# 创建有序分类
s = pd.Series(['B', 'A', 'C'], dtype=pd.CategoricalDtype(categories=['A','B','C'], ordered=True))
print(s > 'B') # 输出: [False, False, True]
# 转换为有序/无序
df['绩效'] = df['绩效'].cat.as_ordered() # 如果原本无序
df['绩效'] = df['绩效'].cat.as_unordered() # 转为无序
# 排序
df_sorted = df.sort_values('绩效')
print(df_sorted[['姓名', '绩效']])
通过将对象类型转为分类,可以显著减少内存占用,特别是重复值多的列。
# 创建一个大型 DataFrame
n = 100000
df_large = pd.DataFrame({
'部门': np.random.choice(['技术', '销售', '管理', '财务'], n),
'评分': np.random.choice(['A','B','C','D'], n)
})
print("转换前内存使用:")
print(df_large.memory_usage(deep=True))
# 转换为分类
df_large['部门'] = df_large['部门'].astype('category')
df_large['评分'] = df_large['评分'].astype('category')
print("转换后内存使用:")
print(df_large.memory_usage(deep=True))
分类变量常用于生成虚拟变量(one-hot encoding)。
# 直接使用 get_dummies
dummies = pd.get_dummies(df['部门'])
print(dummies.head())
# 如果只想对某些分类列生成虚拟变量
df_with_dummies = pd.get_dummies(df, columns=['部门'], prefix='dept')
print(df_with_dummies.head())
import pandas as pd
# 模拟用户满意度调查数据
survey = pd.DataFrame({
'user_id': range(1, 11),
'满意度': ['满意', '非常满意', '一般', '不满意', '满意', '一般', '非常满意', '满意', '不满意', '一般'],
'频次': ['每天', '每周', '每月', '每天', '每天', '从不', '每周', '每月', '每天', '每周']
})
print("原始数据:")
print(survey)
# 1. 将满意度转为有序分类(不满意 < 一般 < 满意 < 非常满意)
满意程度 = ['不满意', '一般', '满意', '非常满意']
survey['满意度'] = pd.Categorical(survey['满意度'], categories=满意程度, ordered=True)
# 2. 将频次转为分类(无需顺序)
survey['频次'] = survey['频次'].astype('category')
# 3. 检查类型
print("\n数据类型:")
print(survey.dtypes)
# 4. 统计各满意度人数
print("\n满意度分布:")
print(survey['满意度'].value_counts())
# 5. 按满意度排序(按顺序)
print("\n按满意度排序:")
print(survey.sort_values('满意度')[['user_id', '满意度']])
# 6. 交叉表:满意度 vs 频次
cross = pd.crosstab(survey['满意度'], survey['频次'])
print("\n交叉表:")
print(cross)
# 7. 内存使用对比(此处数据小,仅示意)
print("\n内存占用:")
print(survey.memory_usage(deep=True))
NaN 表示,不会计入类别中。
add_categories() 扩展类别。
str 方法)中,分类类型需要先转为字符串,可能反而变慢。
.cat.codes 可以获取底层的整数编码,方便机器学习模型输入。union_categoricals() 合并。