crosstab() 默认按字典序排序行列类别而非原始顺序;需用 pd.Categorical 指定 categories 保持顺序;加权统计须用 values+aggfunc='sum';不可哈希类型(如 list)需预处理;margins 合计不对齐常因 NaN 或 dropna 设置不当。

为什么 crosstab() 返回的列名不是原始类别?
默认情况下,pandas.crosstab() 会按字典序对行、列变量的唯一值排序,而不是保留你数据中首次出现的顺序或原始 category 的顺序。这容易让人误以为结果“乱了”,尤其当你依赖列名做后续索引时。
- 用
dropna=False显式控制缺失值是否参与分组(默认True,即自动丢弃NaN) - 若需保持原始顺序,先将变量转为
pd.Categorical并指定categories参数,再传入crosstab() - 列名和行名实际是
Index对象,可直接用.rename()或.reindex()调整,但别在crosstab()外用sort_index()反向覆盖——那只是掩盖问题
如何统计带权重的交叉频数?
crosstab() 本身不接受权重数组,但可以通过 values + aggfunc 绕过:把权重当“值”传进去,再用 sum 聚合。本质是把频数统计升级为加权求和。
- 必须同时指定
values(权重列名)和aggfunc='sum',否则报ValueError: values must be specified when aggfunc is not None - 若权重是整数且想还原为“等效频数”,结果仍是浮点型,需手动
.astype(int)(注意 NaN 权重会导致对应格子为NaN) - 性能上比纯布尔频数慢约 2–3 倍,大数据量时建议先
groupby().size()再unstack()手动加权
pd.crosstab(df['gender'], df['region'], values=df['weight'], aggfunc='sum')
遇到 TypeError: unhashable type: 'list' 怎么办?
这个错几乎只发生在某列含 list、dict、numpy array 等不可哈希类型时。crosstab() 内部要用这些值做分组键,而 Python 不允许 list 当 dict key。
- 检查出错列:用
df[col].apply(type).unique()快速定位是否混入了list - 简单修复:用
df[col].astype(str)强制转字符串(适合探索阶段),但会丢失结构信息 - 严谨做法:提前标准化数据——比如把 list 展平成多行(用
explode()),或提取关键字段(如df[col].str.len()代替原 list) - 别试图在
crosstab()里用 lambda 清洗,它不支持对输入列做 on-the-fly 转换
用 margins=True 后总和对不上?
边际合计(margins)默认是对当前分组层级做 sum,但若原始数据有缺失、重复或 NaN 未显式处理,行列小计之和可能不等于总计。这不是 bug,是聚合逻辑的自然结果。
立即学习“Python免费学习笔记(深入)”;
-
margins=True实际等价于分别加一行一列,再各自调用sum(axis=0/1),不保证数学守恒 - 若需严格守恒,先确保两变量都无
NaN(dropna=True),或用pd.crosstab(..., margins=True, dropna=False)显式把NaN当一类参与统计 - 注意:
margins_name默认是'All',但如果你改了它(比如设为'Total'),后续用.loc['Total']提取时必须匹配该字符串
df[col].isna().sum() 和 df[col].nunique(),比直接重跑 crosstab() 更快。


















