首页 / Pandas 入门教程 / 哑变量与类别编码

Pandas 入门教程

哑变量与类别编码

本教程共 54 篇 · 第 32 篇 · 更新于 2026-08-11 · 约 6 分钟阅读

PandasPandas 入门教程get_dummiesfactorize哑变量类别编码

本节目标:会用 get_dummies 生成哑变量、用 factorize 做整数编码,理解 prefix、drop_first、稀疏存储各自的用途。

为什么需要哑变量

很多统计模型和机器学习算法只认数值,不认字符串。“城市:北京/上海/广州”这种列,直接丢给模型没法算。

哑变量(dummy variable,也叫独热编码 one-hot)的思路:每个取值变成一列,原值出现在哪一行,哪列填 1,其余填 0。北京那行,北京列是 1,上海、广州列是 0。

get_dummies:生成哑变量

对 Series 用 get_dummies:

import pandas as pd

s = pd.Series(["a", "b", "a", "c", "a"])
print(pd.get_dummies(s))

结果三列 a、b、c,列名就是原始取值,每行只有一个 True。默认返回布尔值,想要 0/1 数值用 dtype 参数:pd.get_dummies(s, dtype=“int”)。列的顺序按类别升序排,想让顺序固定,先把列转成 Categorical(§50)再编码。

直接对整个 DataFrame 调用,会自动挑出字符串列、分类列做编码,数值列(int/float)原样保留:

df = pd.DataFrame({
    "城市": ["北京", "上海", "北京"],
    "销售额": [1200, 900, 1500],
})
print(pd.get_dummies(df))

结果”城市”变成城市_北京、城市_上海 两列,“销售额”不动。列名规则是”原列名 + 分隔符 + 取值”,默认分隔符是下划线。

连续数值先分箱再哑变量化也是常见组合:pd.get_dummies(pd.cut(pd.Series([10, 15, 23, 59]), bins=[0, 18, 35, 70])),分箱后的区间变成列名,落在哪个区间哪列为 True。分箱本身见 §50。

编码的步骤通常是这样:

  1. 想清楚哪些列要编码;
  2. 决定列名怎么起(前缀、分隔符);
  3. 判断要不要 drop_first、要不要稀疏存储。

只编码指定列:columns 参数

只想编码指定列时用 columns 参数,其它列即使有字符串也不动:

print(pd.get_dummies(df, columns=["城市"]))
Tip

columns 参数对数值列也生效:pd.get_dummies(df, columns=[“销售额”]) 会把数值列也按取值炸开。一般只对类别列用。

prefix:给哑变量加前缀

需要明确区分哑变量来自哪列时,用 prefix 指定前缀:

print(pd.get_dummies(df, columns=["城市"], prefix="city"))

列名变成 city_北京、city_上海。prefix 可以传字符串(所有列同一个前缀)、列表(每列一个前缀)、字典(按列名指定前缀):

df2 = pd.DataFrame({"城市": ["北京", "上海"], "等级": ["A", "B"]})
print(pd.get_dummies(df2, prefix={"城市": "city", "等级": "level"}))

分隔符用 prefix_sep 改,比如 prefix_sep=”-”。

Note

一列只有一个取值时,get_dummies 会直接省略这列——全 1 的列没有信息量。这是正常行为。

drop_first:丢掉一列防共线

统计建模(比如线性回归)时有个坑:k 个取值生成 k 列哑变量,这 k 列加起来恒等于 1,存在完全共线性,模型会出问题。解法是丢掉第一列(按出现顺序的第一列),剩下的 k-1 列就够——全 0 的行自然代表被丢掉的类别。

print(pd.get_dummies(s, drop_first=True))

结果只剩 b、c 两列,原来”a”的那行两列都是 False。给统计模型喂数据时,drop_first=True 几乎是标配。

稀疏哑变量:省内存

哑变量矩阵有个特点:绝大多数是 0。取值多、数据量大时,普通存储非常浪费内存。

get_dummies 的 sparse=True 参数可以直接生成稀疏哑变量(3.0.5 仍可用)。也可以先生成普通列,再手动转成稀疏类型:

dummies = pd.get_dummies(s, dtype="int")
sparse = dummies.astype("Sparse[int]")
print(sparse)
print(sparse.memory_usage(deep=True).sum())

稀疏存储的本质:只保存非零位置的下标和值,零值不占空间。转成稀疏后,算术运算、布尔筛选照常支持,只是底层存储不同。用 memory_usage 对比就能看到差距。0 占绝对多数、数据量大时值得用;小数据不必折腾。

factorize:编码成整数

另一种常见编码是整数编码:每个不同取值映射成 0、1、2……

codes, uniques = pd.factorize(pd.Series(["A", "A", "B", "A", "C"]))
print(codes)    # [0 0 1 0 2]
print(uniques)  # Index(['A', 'B', 'C'])

返回值有两个:codes 是每个位置的编号,uniques 是编号到原始值的映射(0→A,1→B,2→C)。缺失值编码成 -1。

factorize 的编号按”首次出现顺序”分配,想按字母序编号可以加 sort=True。uniques 常被留着当对照表,比如画图时把刻度标签映射回原名。它适合”只要编号、不关心列展开”的场景;get_dummies 适合模型输入。哑变量想还原成原始类别,用 pd.from_dummies 可以逆回去(列名需要带可解析的分隔符):

back = pd.DataFrame({"city_北京": [1, 0], "city_上海": [0, 1]})
print(pd.from_dummies(back, sep="_"))

三种方式怎么选:要 0/1 矩阵给模型用 get_dummies;只要编号、想省内存用 factorize;要保留类别顺序、参与统计运算用 Categorical(§50)。编码需求简单时,从 get_dummies 和 factorize 入手就够。

小结

get_dummies 把类别列拆成 0/1 哑变量:columns 指定列、prefix 起名、drop_first 防共线、sparse 省内存。factorize 只做整数编码,返回 codes 和 uniques 两件套。给模型喂数据用 get_dummies,只要编号省内存用 factorize,留在表里做分析用 Categorical(§50)。