
`pd.crosstab` 默认会丢弃未在数据中实际出现的分类级别,需显式设置 `dropna=false` 才能完整显示 categorical 的全部 categories。
在使用 pandas.crosstab 对分类数据(Categorical)进行交叉表统计时,一个常见误区是认为只要输入列是 CategoricalDtype 类型(含预定义的完整 categories),输出就自动包含所有级别。实际上,默认行为仍会过滤掉未在观测数据中出现的类别——这与 value_counts() 等方法的行为不同。
关键在于:crosstab 的 dropna 参数默认为 True,它不仅影响缺失值(NaN)的处理,也控制是否保留空分类级别。只有当 dropna=False 时,crosstab 才会严格遵循输入 Categorical 的 categories 属性,将所有预设类别(包括零频次的)纳入行/列索引,并用 0 填充对应单元格。
以下是一个完整示例:
import pandas as pd
from pandas.api.types import CategoricalDtype
# 定义包含 "a", "b", "c" 的完整类别
ctype = CategoricalDtype(categories=["a", "b", "c"])
df = pd.DataFrame({
"x": ["a", "a", "a"],
"y": ["b", "a", "b"]
}, dtype=ctype)
# ❌ 默认 dropna=True → 仅显示实际出现的类别
print("默认 dropna=True:")
print(pd.crosstab(df["x"], df["y"]))
# 输出:
# y a b
# x
# a 1 2
# ✅ 显式设置 dropna=False → 补全所有 categories
print("\n设置 dropna=False:")
print(pd.crosstab(df["x"], df["y"], dropna=False))
# 输出:
# y a b c
# x
# a 1 2 0
# b 0 0 0
# c 0 0 0⚠️ 注意事项:
- dropna=False 是必要且充分条件:仅声明 CategoricalDtype 不够,必须配合该参数;
- 若 index 或 columns 中任一输入为 Categorical,则 dropna=False 会同时作用于二者的所有预设类别;
- 对非 Categorical 输入(如普通字符串 Series),dropna=False 仅影响 NaN 处理,不会引入新标签;
- 该行为适用于 pd.crosstab 所有版本(≥1.0.0),但旧版文档曾表述模糊,建议以官方最新文档为准。
总结:要确保交叉表完整呈现分类变量的语义结构(尤其在建模前特征对齐、报表一致性等场景),务必在调用 crosstab 时显式指定 dropna=False。










