Python实现交叉表类别总计并添加年度状态总计的问题
为Pandas交叉表添加年份状态总计行的解决方案
我用以下Python代码生成随机数据并计算交叉表,已经为交叉表添加了列总计(Total列):
import random import pandas as pd # List of years years = [2000, 2001, 2003] # List of categories and statuses categories = ['CatA', 'CatB', 'CatC'] statuses = ['Active', 'Inactive'] # Empty lists to store the data data = [] # Generate random data for each year for year in years: for _ in range(3): category = random.choice(categories) status = random.choice(statuses) data.append([year, category, status]) # Create a DataFrame to store the data df = pd.DataFrame(data, columns=['Year', 'Category', 'Status']) #Create crosstab cross_tab = pd.crosstab([df.Year, df.Status], df.Category) #Sum columns category_totals = cross_tab.sum(axis=1) cross_tab = pd.concat([category_totals, cross_tab], axis=1) cross_tab.rename(columns={0: 'Total'}, inplace=True) #Print cross_tab
现在我希望为每个年份添加对应的状态总计行,期望效果如下:
| Year | Status | Total | CatA | CatB | CatC |
|---|---|---|---|---|---|
| 2000 | Total | 3 | 1 | 1 | 1 |
| Active | 2 | 1 | 0 | 1 | |
| Inactive | 1 | 0 | 1 | 0 | |
| 2001 | Total | 3 | 2 | 1 | 0 |
| Active | 1 | 1 | 0 | 0 | |
| Inactive | 2 | 1 | 1 | 0 | |
| 2003 | Total | 3 | 0 | 2 | 1 |
| Active | 1 | 0 | 0 | 1 | |
| Inactive | 2 | 0 | 2 | 0 |
我通过year_totals = cross_tab.sum(level="Year")计算出了状态总计数据,但使用pd.concat()合并两个DataFrame时,因索引层级不同失败,请问该如何解决?
解决思路
问题核心是总计行的索引为单层(仅Year),而原交叉表是双层索引(Year+Status),结构不匹配导致合并失败。需要先将总计行的索引转换为双层结构,再合并后重新排序索引。
完整修改代码
import random import pandas as pd # 生成数据部分保持不变 years = [2000, 2001, 2003] categories = ['CatA', 'CatB', 'CatC'] statuses = ['Active', 'Inactive'] data = [] for year in years: for _ in range(3): category = random.choice(categories) status = random.choice(statuses) data.append([year, category, status]) df = pd.DataFrame(data, columns=['Year', 'Category', 'Status']) # 创建交叉表并添加列总计 cross_tab = pd.crosstab([df.Year, df.Status], df.Category) category_totals = cross_tab.sum(axis=1) cross_tab = pd.concat([category_totals, cross_tab], axis=1) cross_tab.rename(columns={0: 'Total'}, inplace=True) # 计算年份总计行,并转换为双层索引 year_totals = cross_tab.sum(level="Year") # 为总计行构造(Year, 'Total')格式的双层索引 year_totals.index = pd.MultiIndex.from_tuples( [(year, 'Total') for year in year_totals.index], names=['Year', 'Status'] ) # 合并原交叉表和总计行 combined = pd.concat([cross_tab, year_totals]) # 重新排序索引,让每个年份的Total行排在最前面 combined = combined.sort_index(level=['Year', 'Status'], key=lambda x: x == 'Total', ascending=False) # 可选:优化显示,同一Year下非Total行的Year值设为空 combined = combined.reset_index() combined['Year'] = combined['Year'].where(combined['Status'] == 'Total', '') combined = combined.set_index(['Year', 'Status']) print(combined)
关键步骤说明
- 索引结构统一:用
pd.MultiIndex.from_tuples将总计行的单层Year索引转换为(Year, 'Total')的双层索引,和原交叉表索引结构完全匹配。 - 合并与排序:合并后通过
sort_index的key参数,让每个年份的'Total'行优先排在该年份的Active/Inactive行之前。 - 显示优化:通过重置索引再重新设置的方式,将同一Year下非Total行的Year值置空,完全匹配期望的表格显示效果。
内容的提问来源于stack exchange,提问作者as_meth
相关产品推荐
相关产品推荐

