
本文介绍三种简洁高效的pandas方法,将问卷类yes/no分类数据按题目维度统计响应频次,并输出为“响应类型×题目”的标准宽格式汇总表,适用于报告生成与可视化前置处理。
在处理结构化问卷数据时,常需将原始的长形响应记录(每行一个客户、每列一个问题)转化为按响应类别(如 Yes/No)横向汇总的统计表。这种宽格式结果便于快速比较各题倾向性,也更适合作为图表输入或嵌入业务看板。
以下提供三种推荐方案,兼顾简洁性、通用性与可读性:
✅ 方法一:布尔求和法(最简,仅适用于二元分类)
适用于严格只有 'Yes' 和 'No' 的场景,性能最优:
import pandas as pd
# 假设 df 为原始数据框
questions = df.filter(like='Question') # 提取所有 Question_* 列
yes_counts = questions.eq('Yes').sum() # 每列中 'Yes' 的数量(自动转布尔后求和)
no_counts = len(df) - yes_counts # 推导 'No' 数量
result = pd.DataFrame({'Yes': yes_counts, 'No': no_counts}).T
result.index.name = 'Response'✅ 优势:代码极简、执行快;❌ 局限:仅支持明确二值,无法扩展至多类(如 'Yes'/'No'/'Maybe')。
✅ 方法二:melt + value_counts(通用稳健)
无需预设类别,自动识别全部唯一值,推荐作为默认选择:
melted = df.filter(like='Question').melt(var_name='Question', value_name='Response')
result = (melted.value_counts(['Response', 'Question'])
.unstack('Question', fill_value=0)
.rename_axis(index=None, columns=None))此方法先将问题列“熔解”为两列(Question, Response),再对组合频次计数,最后透视为宽表。自动覆盖所有响应值(包括意外空值或新增选项),且 fill_value=0 确保缺失组合补零。
✅ 方法三:crosstab(语义清晰,专为交叉表设计)
语义最直观,适合强调“响应 × 题目”关系的场景:
stacked = df.filter(like='Question').stack() # 转为 Series,索引含 (client_id, question) result = pd.crosstab(stacked, stacked.index.get_level_values(1)) result.index.name = 'Response' result.columns.name = None
? 注意事项与最佳实践
- 列筛选安全:使用 df.filter(like='Question') 比硬编码列名更健壮,避免因列顺序变动或新增字段导致错误;
- 索引对齐:所有方法均保持题目列为列名、响应类型为行索引,与目标格式完全一致;
- 空值处理:melt 和 crosstab 默认忽略 NaN;若需统计空值,可在 melt() 中加 ignore_index=False 并提前用 fillna() 处理;
- 扩展性提示:如需添加百分比列,可在 result 后追加 result.div(result.sum(axis=0), axis=1).mul(100).round(1)。
无论选择哪种方式,最终均可直接导出为 Excel 或传入 Seaborn/Matplotlib 进行热力图、堆叠柱状图等可视化,真正实现从原始数据到决策就绪报表的一站式转换。










