分类数据与分箱
Pandas 中的 categorical 类型本质上是一个有限取值集合(vocabulary)。它可以减少内存占用、明确允许的值,并保留有意义的类别顺序。
import pandas as pd
from pandas.api.types import CategoricalDtype
tasks = pd.DataFrame({"priority": ["high", "low", "urgent", None]})
priority_type = CategoricalDtype(
categories=["low", "medium", "high"],
ordered=True,
)
unknown = tasks["priority"].notna() & ~tasks["priority"].isin(priority_type.categories)
assert tasks.loc[unknown, "priority"].tolist() == ["urgent"]
tasks["priority"] = tasks["priority"].astype(priority_type)
**有序(Ordered)**分类数据支持基于声明顺序的排序和范围比较(如 >, <)。**无序(Unordered)**分类数据仅表示成员关系,不隐含任何大小或先后逻辑。
校验取值范围
当数据转换为 categorical 类型时,任何不在 categories 列表中的值都会变成 NaN。
务必在转换前或转换后立即检查是否存在未知值,并明确区分“未知类别”与“数据本身缺失”这两种情况。
连续值分箱
将连续数值转换为离散区间是常见的预处理步骤。
ages = pd.DataFrame({"age": [-1, 0, 17, 18, 35, 65]})
ages["band"] = pd.cut(
ages["age"],
bins=[0, 18, 35, 65, float("inf")],
labels=["child", "young_adult", "adult", "senior"],
right=False,
)
ages["quartile"] = pd.qcut(ages["age"], q=4, duplicates="drop")
pd.cut:基于业务含义或固定数值边界划分区间。pd.qcut:基于样本分位数划分,尽量使各区间的样本数量接近;重复值可能限制划分结果。
注意:分箱会丢失原始精度,可能导致微小的输入变化在输出上表现为跳变。务必保留原始数值列,以便后续分析或回溯。同时,记录清楚区间的闭合方式(左闭右开等)和边缘处理策略。
模型编码
pd.get_dummies 可以生成独热编码(One-Hot Encoding)列。
但在生产环境中,编码逻辑应封装在模型的训练与推理流水线内部,以确保训练集和推理集使用完全一致的类别词汇表。
切勿让生产环境的每个批次独立推断特征列,这会导致特征空间不一致,引发模型预测错误。
边界与类别编码
分类示例在转换前识别出 urgent;转换后的值依次为 high、low、缺失、缺失。声明类别集合不会自动拒绝非法行。类别编码只是存储位置,缺失编码为 -1;它们不是测量值,求平均通常没有意义。是否节省内存取决于值的重复程度,而不只是是否使用 category。
cut 设置 right=False 后,区间左闭右开:0 岁为 child,18 岁为 young_adult,35 岁为 adult,65 岁为 senior;-1 不在分箱范围内,变成缺失。传入 Series 会返回索引对齐的分类 Series,传入列表通常返回 Categorical。qcut 删除重复分位点后,可能不足四箱;相同数值未必能分成等量的箱。训练/测试场景可用 retbins=True 取得训练边界,再用 cut 复用固定边界,同时明确后续越界值的处理方式。