pandas.crosstab() 结果与手动统计不符的主因是默认丢弃NaN且不包含未出现的类别组合;需设dropna=False或预设Categorical类别以保留全组合,行列顺序默认按升序而非原始顺序。

为什么 pandas.crosstab() 统计结果和手动数的对不上
常见原因是默认丢弃了空值(NaN)且不包含未出现的类别组合。比如 A 列有 ['x', 'y', 'z'],B 列有 ['p', 'q'],但实际数据里没有 'z' 和 'q' 的组合,crosstab() 就不会在结果里留一行一列补 0。
- 加参数
dropna=False让NaN当作普通值参与统计(注意:会多出一行/列标为NaN) - 如果想强制保留所有可能的组合(包括零频次),得配合
pd.Categorical预设类别:df['A'] = pd.Categorical(df['A'], categories=['x','y','z'])<br>df['B'] = pd.Categorical(df['B'], categories=['p','q'])<br>pd.crosstab(df['A'], df['B'])
- 不设
normalize时返回的是整型频数;设成True或'index'/'columns'后变成浮点比例,类型变了,别直接拿去算 count
怎么用 crosstab() 统计多列交叉或带条件的分组
它原生只接受两个一维序列(index 和 columns),但可以靠 pd.cut()、df.groupby().size() 或嵌套 crosstab() 拆解复杂需求。
- 想按区间分组再交叉?先用
pd.cut(df['age'], bins=[0,18,35,60])生成离散列,再传给crosstab() - 三列想看 A×B 在 C=1 下的分布?别硬塞三列进
crosstab(),先过滤:pd.crosstab(df[df['C']==1]['A'], df[df['C']==1]['B']) - 要加权重(比如每行代表不同数量样本)?用
values+aggfunc='sum':pd.crosstab(df['A'], df['B'], values=df['weight'], aggfunc='sum')
crosstab() 返回的 DataFrame 行列顺序为啥不按原始数据顺序
默认按各列值的升序排列,不是按首次出现顺序。这对字符串或分类变量容易造成误解——比如月份字段 'Jan'/'Feb'/'Mar' 会被排成 'Feb'/'Jan'/'Mar'。
- 解决办法是提前把列转成
Categorical并指定categories顺序,crosstab()会尊重这个顺序 - 或者用
reindex()手动调整结果行列:result.reindex(['Jan','Feb','Mar']).reindex(columns=['X','Y']) - 注意:如果原始列含重复值但没设
Categorical,crosstab()仍按字典序排,不是按unique()返回的顺序
性能差、内存爆了?小心这几个隐式开销
crosstab() 内部会做去重、排序、广播匹配,当输入含大量唯一值或长字符串时,比纯 groupby().size().unstack() 更慢、更吃内存。
- 大数据量下优先考虑:
df.groupby(['A', 'B']).size().unstack(fill_value=0)—— 更轻量,且支持observed=True只统计实际出现的组合 - 如果只是要稀疏矩阵(比如做后续聚类),别转成稠密
DataFrame,改用scipy.sparse.csr_matrix构造 - 传入的 Series 如果没重置索引(比如从切片得来),
crosstab()可能因索引对齐失败而静默填充 NaN,导致结果变大










