
本文介绍如何使用pandas的pd.crosstab()配合分类数据,将分组内某列的多个取值频次自动转换为独立计数列,一步完成“宽表聚合”,避免手动groupby+unstack等冗余操作。
本文介绍如何使用pandas的`pd.crosstab()`配合分类数据,将分组内某列的多个取值频次自动转换为独立计数列,一步完成“宽表聚合”,避免手动groupby+unstack等冗余操作。
在数据分析中,常遇到类似场景:原始数据按ID(如店铺)和类别标签(如客户评价等级)记录多条明细,而下游分析需要每个ID对应一行,且为每种可能的类别(如'High'、'Medium'、'Low')单独设一列,填入该ID下该类别的出现次数。这种“长转宽+频次统计”的需求,用pd.crosstab()最为简洁高效。
以下为完整实现步骤:
✅ 步骤1:构造示例数据
import pandas as pd data = [['store1', 'High'], ['store1', 'Medium'], ['store2', 'Low'], ['store2', 'Low']] df = pd.DataFrame(data, columns=['Shop', 'Review']) print(df)
输出:
Shop Review 0 store1 High 1 store1 Medium 2 store2 Low 3 store2 Low
✅ 步骤2:定义有序类别并应用crosstab
关键点在于——显式声明Review列的分类顺序,确保输出列严格按['High', 'Medium', 'Low']排列(否则crosstab默认按字母序排序,可能导致列序错乱):
# 将Review设为有序分类,指定categories顺序 df['Review'] = pd.Categorical(df['Review'], categories=['High', 'Medium', 'Low']) # 生成交叉频数表:行=Shop,列=Review取值 result = pd.crosstab(df['Shop'], df['Review']) print(result)
输出:
Review High Medium Low Shop store1 1 1 0 store2 0 0 2
✅ 步骤3:优化表头与索引(可选)
默认输出中,行索引名为Shop、列索引名为Review。若需更干净的结构(如直接用于建模或导出),可移除轴名称:
result_clean = pd.crosstab(df['Shop'], df['Review']).rename_axis(index=None, columns=None) print(result_clean)
输出:
High Medium Low store1 1 1 0 store2 0 0 2
⚠️ 注意事项
- 类别完整性保障:即使某店铺未出现某类评价(如store1无Low),结果中仍会保留该列为0——这正是Categorical+crosstab的核心优势,避免遗漏维度。
- 性能优势:相比df.groupby(['Shop', 'Review']).size().unstack(fill_value=0),crosstab底层优化更好,尤其在大数据量时更稳定。
- 扩展性提示:若需添加前缀(如列名改为count_High)、重置索引为普通列,可链式调用:.add_prefix('count_').reset_index()。
通过以上三步,你已将原始长表精准、可复现地转换为目标宽表结构,代码简洁、逻辑清晰、结果可控——这是pandas处理“分组频次展开”任务的标准范式。

















