自学教程 学编程从这里开始

Pandas 类别类型

Pandas 类别类型(Categorical)

Categorical 是 Pandas 专为有限重复标签设计的数据类型,核心作用:节省内存、加速分组/筛选/排序、自定义排序顺序。很多报表、维度字段(渠道、商品、状态、地区)都优先使用它。本文独立成文,覆盖创建、属性、有序分类、增删类别、类型转换、性能、坑点,延续整套教程风格。
前置导入

import pandas as pd
import numpy as np

一、什么是 Categorical

Categorical 由两部分组成:

  1. categories:唯一标签列表(类别池)
  2. codes:整数编码,原始数据映射成数字存储
  3. ordered:布尔值,标记是否为有序分类

优势:

  • 重复值多的时候,内存远小于 string / object
  • groupby、筛选、value_counts 速度更快
  • 可以自定义标签顺序,不受字典序控制(报表、图表排序神器)

适用场景:渠道、订单状态、产品分类、省份、评级(低/中/高)
不适合:唯一值极多(订单号、手机号),会额外增加开销

# 简单示例
s = pd.Series(["线上","线下","线上","线下","线上"], dtype="category")
print(s)
print(s.cat.categories)
print(s.cat.codes)

二、创建 Categorical 的几种方式

方式1:创建Series时直接指定 dtype

s = pd.Series(["A","B","A","C"], dtype="category")

方式2:pd.Categorical 构造

cat = pd.Categorical(
    ["低","高","中","低"],
    categories=["低","中","高"],
    ordered=True
)
s = pd.Series(cat)

ordered=True 开启有序,支持比较:"低" < "中" < "高"

方式3:astype 转换(最常用)

df = pd.DataFrame({
    "渠道":["线上","线下","线上","线下"],
    "金额":[178,45,267,90]
})
df["渠道"] = df["渠道"].astype("category")

方式4:read_csv 读取时直接指定(性能最优)

df = pd.read_csv(
    "sales.csv",
    dtype={"渠道":"category"}
)

三、Categorical 核心 cat 访问器属性

s = pd.Series(["低","中","高","低"], dtype=pd.CategoricalDtype(["低","中","高"], ordered=True))

s.cat.categories    # 类别列表
s.cat.codes         # 编码数组,int
s.cat.ordered       # 是否有序 bool
s.cat.rename_categories({"低":"L","中":"M","高":"H"}) # 重命名类别

四、有序分类(重点,报表高频)

普通字符串排序是字典序;有序分类可以自定义业务顺序。

# 优先级:低 < 中 < 高
dtype_rank = pd.CategoricalDtype(
    categories=["低","中","高"],
    ordered=True
)
s = pd.Series(["高","低","中","低"], dtype=dtype_rank)

# 排序会按照我们定义的顺序,不是字典序
s.sort_values()

# 支持大小比较
s > "低"

无序分类不能做 > < >= <= 比较,会报错。

五、修改类别

5.1 新增/删减类别 set_categories

s = pd.Series(["线上","线下"], dtype="category")
# 新增"分销"类别,原有不在列表里的值变成NaN
s = s.cat.set_categories(["线上","线下","分销"])

参数:

  • set_categories(new_categories, ordered=None)
  • 原有值不在新列表 → 转为 NaN

5.2 重命名类别 rename_categories

s = s.cat.rename_categories({"线上":"Online","线下":"Offline"})

5.3 移除未使用类别 remove_unused_categories

数据里已经没有的类别,清理掉,节省内存

s = s.cat.remove_unused_categories()

5.4 追加类别 add_categories

s = s.cat.add_categories("分销")

六、缺失值处理

Categorical 本身支持缺失值 pd.NA,缺失值不会出现在 categories 里,codes 中用 -1 代表空值:

s = pd.Series(["线上", None, "线下"], dtype="category")
print(s.cat.codes) # 对应:0, -1, 1

七、分组、筛选、排序实战

df = pd.DataFrame({
    "等级":["低","高","中","低","高"],
    "金额":[100,900,400,120,850]
})
# 设置有序分类
df["等级"] = df["等级"].astype(pd.CategoricalDtype(["低","中","高"], ordered=True))

# 分组聚合,结果顺序自动按 categories 顺序
df.groupby("等级")["金额"].sum()

# 筛选
df[df["等级"] > "低"]

# 排序
df.sort_values("等级")

八、category 转成其他类型

# 转字符串 string
s_str = s.astype("string")
# 转普通object
s_obj = s.astype(object)
# 拿到编码(数字)
s_code = s.cat.codes

⚠️ 坑:category 类型不能直接使用 str 访问器,需要先转 string。

# ❌ 报错
# s.str.contains("线")
# ✅ 正确写法
s.astype("string").str.contains("线")

九、性能与内存对比

df = pd.DataFrame({"渠道": np.random.choice(["线上","线下","分销"], size=100000)})
print(df["渠道"].astype("object").memory_usage(deep=True))
print(df["渠道"].astype("category").memory_usage(deep=True))

十万行级别,category 内存通常只有 string/object 的 1/5 ~ 1/10。

前提:唯一值数量远小于总行数;如果几乎每行都不一样,category 没有优势。

十、高频坑清单

  1. 有序分类比较报错:没有设置 ordered=True,不能用 > <
  2. str 方法直接调用报错:category 没有 str 访问器,先转 string
  3. 新增标签自动变成 NaN:类别池没有定义这个值,需要 add_categories / set_categories
  4. 排序不是预期顺序:字符串默认字典序,改用有序 CategoricalDtype
  5. 合并两个category列 categories 不一致:运算会自动升级成object
  6. codes 是内部编码,不要当成业务ID使用,categories顺序改动后codes会变
  7. 读取Excel默认读成object,大量重复维度字段记得转category

十一、速查表

API作用
dtype=”category”快速创建无序分类
pd.CategoricalDtype(categories, ordered)自定义有序分类类型
s.cat.categories获取类别列表
s.cat.codes获取底层编码
s.cat.set_categories重新设置类别池
s.cat.add_categories追加新类别
s.cat.remove_unused_categories删除没有出现过的类别
s.cat.rename_categories类别重命名
s.astype(“string”)category转字符串

十二、综合实战模板

def cast_rank_category(series):
    """将评级字段转为有序分类:低 < 中 < 高"""
    dtype = pd.CategoricalDtype(
        categories=["低","中","高"],
        ordered=True
    )
    return series.astype(dtype)
标签:

0 条笔记