Pandas crosstab多列场景添加百分比报错及解决方案咨询
解决多列交叉表添加百分比列的报错问题
问题描述
我尝试创建包含多列的交叉表(crosstab),以下代码可正常生成交叉表:
crt= pd.crosstab(index=df.hc_est, columns=[df.year_iq,df.group], margins=True)
生成的交叉表如下:
year_iq 2017 2018 All
cir_grup inter prim inter prim
hc_est
IA 0 10 1 11 22
IC 0 4 0 0 4
IC1 0 0 0 1 1
....
IVA 4 1 2 0 7
IVB 1 0 1 0 2
All 13 20 12 19 64
我希望为每一列(包括'All'列)添加百分比列,尝试通过设置normalize参数生成带百分比的交叉表再合并,但出现报错:
- 执行
pd.crosstab(index=df.hc_est, columns=[df.year_iq,df.group], margins=True, normalize=True)时,报错:TypeError: Expected tuple, got str - 执行
pd.crosstab(index=df.hc_est, columns=[df.year_iq,df.group], margins=True, normalize='columns')时,报错:ValueError: Length of new names must be 1, got 2 - 使用
normalize='all'选项也会报错:TypeError: Expected tuple, got str
数据类型:hc_est为object,year_iq为Int64,group为object
请问报错原因是什么?是否有其他方法可为每一列添加基于列总计的百分比?
报错原因分析
- 当你使用多层列索引(这里
columns是[df.year_iq,df.group],生成两层列结构)时,normalize参数的字符串取值(比如'columns'、'all')会和多层索引的结构冲突。pandas在处理多层列的交叉表时,期望normalize的取值是和列索引层数匹配的元组,而非单一字符串,这就导致了TypeError: Expected tuple, got str。 - 而指定
normalize='columns'时出现的ValueError: Length of new names must be 1, got 2,是因为pandas尝试给归一化后的结果重命名列,但多层列的长度是2,和字符串参数的长度不匹配,引发了索引长度不一致的错误。
解决方案:手动计算百分比并合并表格
既然直接用normalize参数在多层列交叉表中会出问题,我们可以先生成原始计数交叉表,再手动计算每一列的百分比,最后把计数和百分比列合并起来。
步骤1:生成原始计数交叉表
import pandas as pd # 生成包含总计行的原始交叉表 crt_counts = pd.crosstab(index=df.hc_est, columns=[df.year_iq, df.group], margins=True)
步骤2:计算基于列总计的百分比
我们用交叉表的数值部分除以每一列的总计(也就是最后一行All的值),再转换为百分比格式并保留两位小数:
# 计算百分比,排除最后一行的总计(总计行不需要计算占比) crt_pct = crt_counts.iloc[:-1] / crt_counts.iloc[-1] * 100 crt_pct = crt_pct.round(2) # 给百分比表添加总计行,对应值设为100(总计的占比就是100%) crt_pct.loc['All'] = 100.0
步骤3:合并计数和百分比列
我们可以通过构建三层列索引,把每一列的计数和对应的百分比放在相邻位置:
# 创建新的三层列索引,第三层区分计数和百分比 new_columns = [] for col in crt_counts.columns: new_columns.append((col[0], col[1], 'Count')) new_columns.append((col[0], col[1], 'Pct(%)')) # 初始化合并后的DataFrame combined_df = pd.DataFrame(index=crt_counts.index, columns=pd.MultiIndex.from_tuples(new_columns)) # 填充计数数据 for col in crt_counts.columns: combined_df[(col[0], col[1], 'Count')] = crt_counts[col] # 填充百分比数据 for col in crt_pct.columns: combined_df[(col[0], col[1], 'Pct(%)')] = crt_pct[col]
最终效果
这样你会得到一个三层列索引的交叉表,每一组原始列后面都会跟着对应的百分比列,包括All列的百分比(固定为100%),比如2017-inter列会拆分为2017-inter-Count和2017-inter-Pct(%),清晰展示计数和占比情况。
内容的提问来源于stack exchange,提问作者xavis
相关产品推荐
相关产品推荐

