一句话总结

pd.crosstab() 用于计算两个或多个分类变量的频数交叉表(列联表),是 pivot_table 的便捷特化版本,也可计算占比、均值等聚合统计。

1. crosstab() 基本语法

pd.crosstab(index, columns, values=None, rownames=None, colnames=None,
            aggfunc=None, margins=False, margins_name='All', dropna=True,
            normalize=False, sort=True)

参数详解

参数说明
index行分组键:类数组、Series、列名列表等
columns列分组键:类数组、Series、列名列表等
values可选,用于聚合的数值数组/DataFrame 列
rownames行索引名称(当 index 为 Series/数组时设置)
colnames列索引名称
aggfunc指定 values 后使用的聚合函数,如 'sum'、'mean'、np.mean
margins是否添加总计行列,默认 False
margins_name总计名称,默认 'All'
dropna是否剔除全为 NaN 的行,默认 True
normalize归一化:False(频数)、'all'(整体占比)、'index'(行占比)、'columns'(列占比)、True(等价 'all')
sort是否对结果排序,默认 True

与 pivot_table 的关系

当 crosstab() 的 values 和 aggfunc 参数同时给出时,其行为等价于:

df.pivot_table(index=index, columns=columns, values=values, aggfunc=aggfunc)

区别在于 crosstab() 可直接接收独立的 Series/数组作为输入,无需先在 DataFrame 中。

2. 基本示例

频数交叉表

import pandas as pd
 
df = pd.DataFrame({
    'gender': ['男', '女', '男', '女', '男', '女'],
    'grade':  ['A', 'B', 'B', 'A', 'A', 'B'],
    'score':  [88, 76, 91, 85, 92, 79]
})
 
ct = pd.crosstab(df['gender'], df['grade'])
print(ct)
# grade  A  B
# gender
# 女      1  2
# 男      2  1

多列交叉(行/列为列表)

# 行:gender;列:grade + score 分箱
pd.crosstab(df['gender'], [df['grade'], df['score']])

添加名称 rownames / colnames

pd.crosstab(df['gender'], df['grade'],
            rownames=['性别'], colnames=['成绩等级'])

3. 带聚合的交叉表

使用 values + aggfunc

ct_mean = pd.crosstab(df['gender'], df['grade'],
                      values=df['score'], aggfunc='mean')
print(ct_mean)
# grade      A     B
# gender
# 女       85.0  77.5
# 男       90.0  91.0

多个聚合函数

# 使用 aggfunc 字典(较新版本支持列表)
pd.crosstab(df['gender'], df['grade'],
            values=df['score'], aggfunc='sum')

4. normalize 归一化

# 整体占比
pd.crosstab(df['gender'], df['grade'], normalize='all')
# 每行占比(行归一化)
pd.crosstab(df['gender'], df['grade'], normalize='index')
# 每列占比(列归一化)
pd.crosstab(df['gender'], df['grade'], normalize='columns')

5. margins 总计

pd.crosstab(df['gender'], df['grade'], margins=True, margins_name='合计')
# 在行尾和列尾添加"合计"

6. 实战:多维度交叉分析

# 更复杂的数据
df2 = pd.DataFrame({
    'department': ['技术', '技术', '市场', '市场', '技术', '市场'] * 2,
    'level': ['初级', '高级', '初级', '高级', '中级', '中级'] * 2,
    'satisfaction': ['高', '低', '高', '高', '中', '低'] * 2,
    'salary': [8000, 15000, 7000, 16000, 11000, 9000] * 2
})
 
# 二维交叉:部门 × 级别 的满意度频数
cross1 = pd.crosstab(df2['department'], df2['level'])
 
# 三维交叉:部门 × (级别, 满意度)
cross2 = pd.crosstab(df2['department'], [df2['level'], df2['satisfaction']])
 
# 行占比 + 总计
cross3 = pd.crosstab(df2['department'], df2['level'],
                     normalize='index', margins=True)

7. 注意事项

易错点

  • index 和 columns 参数名是位置参数,第一个位置对应 index,第二个对应 columns。
  • normalize 与 margins 同时使用时,总计行列基于归一化后的数值。
  • values + aggfunc 若无匹配数据,结果会产生 NaN,可用 fillna(0) 处理。

应用场景

  • 探索性分析中快速了解两个分类变量的关系
  • 数据质量检查:查看类别组合的覆盖情况
  • 机器学习特征工程中生成交叉特征

相关笔记