使用 crosstab 创建频数表
使用 pd.crosstab 计算两个分类列的频数或比例交叉表。
使用 crosstab 创建频数表 是 CoddyKit 上的免费 Pandas & NumPy Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Pandas & NumPy Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Pandas & NumPy Academy 课程共包含 4 节课。
什么是交叉表?
交叉制表(crosstab)用于统计数据集中两个或更多分类变量的各个取值组合出现的次数。它是专门用于计数的一种数据透视表。Pandas 提供了 pd.crosstab(),可以简洁地计算这些频数表,无需显式调用 groupby() 或 pivot_table()。
基本的 crosstab() 调用
最简形式的 pd.crosstab(index, columns) 调用接收两个类似数组的输入(通常是 DataFrame 列),并返回一个频数表。行对应第一个参数中的唯一值,列对应第二个参数中的唯一值。每个单元格记录原始数据中两个值同时出现的行数。
import pandas as pd
df = pd.DataFrame({
'gender': ['M', 'F', 'M', 'F', 'M', 'F', 'M'],
'product': ['A', 'A', 'B', 'B', 'A', 'B', 'B']
})
ct = pd.crosstab(df['gender'], df['product'])
print(ct)
# product A B
# gender
# F 1 2
# M 2 2自定义行名和列名
使用 rownames 和 colnames 参数,为输出中的行轴和列轴指定有意义的名称,从而覆盖默认的 Series 名称。当 DataFrame 中的原始列名无法清晰说明其在结果表中的含义时,这一功能非常有用。
ct = pd.crosstab(
df['gender'], df['product'],
rownames=['Customer Gender'],
colnames=['Purchased Product']
)
print(ct)
# Purchased Product A B
# Customer Gender
# F 1 2
# M 2 2添加边际汇总(行总计和列总计)
传入 margins=True,即可添加汇总所有值的行和列。边际标签默认为 'All',但可以使用 margins_name 自定义。边际行显示每列的总计,边际列显示每行的总计,右下角的单元格显示总计。
ct = pd.crosstab(df['gender'], df['product'],
margins=True, margins_name='Total')
print(ct)
# product A B Total
# gender
# F 1 2 3
# M 2 2 4
# Total 3 4 7标准化为比例
传入 normalize 参数,可以将计数转换为比例。normalize=True 或 normalize='all' 会除以总计。normalize='index' 会计算行比例(每行的和为 1.0)。normalize='columns' 会计算列比例(每列的和为 1.0)。当各组大小不同时,比例通常比原始计数更有信息量。
# Row proportions: what fraction of each gender bought each product?
print(pd.crosstab(df['gender'], df['product'], normalize='index').round(2))
# product A B
# gender
# F 0.33 0.67
# M 0.50 0.50
# All proportions: each cell as fraction of grand total
print(pd.crosstab(df['gender'], df['product'], normalize=True).round(2))聚合值而不是计数
默认情况下,crosstab 会统计行数。您也可以通过传入 values 和 aggfunc 参数,对数值列进行聚合,这与 pivot_table() 类似。例如,可以计算每种性别与产品组合的总收入。这样一来,crosstab 几乎等同于 pivot_table,但在频数表场景中语法略为简洁。
df['revenue'] = [100, 80, 150, 120, 200, 90, 130]
# Sum revenue by gender and product instead of counting
ct_rev = pd.crosstab(
df['gender'], df['product'],
values=df['revenue'],
aggfunc='sum'
)
print(ct_rev)
# product A B
# gender
# F 80 210
# M 300 280多级交叉表
向 index 或 columns 传入列表,可以创建行或列包含多个级别的交叉表。结果会包含 MultiIndex,从而提供更细致的分类。例如,在行上按性别和地区进行交叉制表、在列上按产品进行交叉制表,可以显示每种性别—地区—产品组合。
df['region'] = ['East', 'West', 'East', 'West', 'East', 'East', 'West']
ct_multi = pd.crosstab(
[df['gender'], df['region']],
df['product'],
margins=True
)
print(ct_multi)
# product A B All
# gender region
# F East 0 1 1
# West 1 1 2
# M East 2 1 3
# West 0 1 1
# All 3 4 7crosstab() 与 pivot_table()
pd.crosstab() 和 pd.pivot_table() 的功能有很大重叠。crosstab 专为频数统计优化,可以直接接收类似数组的输入(而不是 DataFrame),因此用于快速制表时略为简洁。pivot_table 作用于 DataFrame,并且原生支持任意聚合函数。对于纯计数任务,crosstab 是更符合惯用方式的选择;对于数值聚合,建议使用 pivot_table。
# crosstab (more concise for counting)
print(pd.crosstab(df['gender'], df['product']))
# Equivalent pivot_table
print(pd.pivot_table(df, index='gender', columns='product',
aggfunc='size', fill_value=0))
# Both produce the same result卡方检验准备
交叉表是独立性卡方检验的标准输入,该检验用于判断两个分类变量在统计上是否相关。scipy.stats.chi2_contingency() 函数可以直接接收交叉表数组。这是 crosstab 在统计数据分析中最常见的用途之一:先计算表格,然后在同一流程中进行检验。
from scipy import stats
ct = pd.crosstab(df['gender'], df['product'])
# Convert to numpy array for scipy
chi2, p, dof, expected = stats.chi2_contingency(ct.values)
print(f'Chi2: {chi2:.2f}')
print(f'P-value: {p:.4f}')
print(f'Degrees of freedom: {dof}')
# p > 0.05 means no significant association将交叉表可视化为热力图
类别较多的交叉表很难通过原始数字进行解读。使用 sns.heatmap() 将其可视化为热力图,可以立即看出其中的模式——频数较高的单元格会显示为明亮的颜色。传入标准化后的版本可以显示比例而不是原始计数,并使用 annot=True 在每个单元格中显示数值。
import seaborn as sns
import matplotlib.pyplot as plt
ct_norm = pd.crosstab(df['gender'], df['product'], normalize='index')
# sns.heatmap(ct_norm, annot=True, fmt='.0%', cmap='Blues')
# plt.title('Purchase Rate by Gender and Product')
# plt.tight_layout()
# plt.show()
print('Normalised crosstab ready for heatmap:')
print(ct_norm.round(2))实用示例:调查分析
完整的 crosstab 工作流:加载调查数据,计算人口统计变量与响应变量之间的频数表,按行标准化以获得响应率,并识别其中的显著模式。这是市场研究、A/B 测试和用户细分中的标准分析流程,使用不到 10 行 Pandas 代码即可完成。
# Simulate a survey dataset
survey = pd.DataFrame({
'age_group': ['18-25', '26-35', '18-25', '36-45', '26-35', '36-45'],
'satisfaction': ['High', 'Low', 'High', 'Medium', 'High', 'Low']
})
ct = pd.crosstab(survey['age_group'], survey['satisfaction'],
margins=True, margins_name='Total')
print(ct)
rates = pd.crosstab(survey['age_group'], survey['satisfaction'],
normalize='index').round(2)
print(rates)快速检查
测试您对本课中用于生成频数表的 pd.crosstab 的理解。
课程回顾
在本课中,您学习了:pd.crosstab() 会计算两个分类变量组合的频数;normalize 会将频数转换为行比例、列比例或总比例;margins=True 会添加行和列的总计;此外,交叉表输出可以直接用于卡方检验和热力图可视化。接下来,我们将使用 DatetimeIndex 和周期范围处理时间序列数据。
常见问题解答
「使用 crosstab 创建频数表」课时是免费的吗?
是的 — 「使用 crosstab 创建频数表」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Pandas & NumPy Academy 课程的其余内容,请升级到 CoddyKit PRO。 Pandas & NumPy Academy 课程共包含 4 节课。
「使用 crosstab 创建频数表」这节课中我会学到什么?
使用 pd.crosstab 计算两个分类列的频数或比例交叉表。 你通过在浏览器中直接运行的动手代码来练习 Pandas & NumPy Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Pandas & NumPy Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Pandas & NumPy Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「使用 crosstab 创建频数表」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Pandas & NumPy Academy 课中编写并运行代码吗?
能。每节 Pandas & NumPy Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- pivot_table:交叉表
- melt:从宽格式转换为长格式
- 使用 MultiIndex 进行 stack 和 unstack
- 使用 crosstab 创建频数表