Pandas:如何在多个透视表中包括所有列和所有索引

3
我正在尝试从以下数据框(df)创建不同位置的数据透视表:
位置 类别 状态 价格
1 家具 $100
1 家具 $50
2 办公用品 $200
1 家具 $100
1 办公用品 $300
1 办公用品 $150

首先,我使用以下代码对数据框进行了筛选,以分别获取位置为1和2的数据:

df1 = df[df['Location'] == 1]

df2 = df[df['Location'] == 2]

接下来我使用了标准的pandas透视表函数:

pd.pivot_table(df1, values='Price', index='Status', columns='Category', aggfunc=np.sum)

pd.pivot_table(df2, values='Price', index='Status', columns='Category', aggfunc=np.sum)

我有以下两个数据透视表的输出:
位置1: | 状态 | 家具 | 办公用品 | | --- | --- | --- | | 新 | $200 | $300 | | 旧 | $50 | $150 |
位置2: | 状态 | 办公用品 | | --- | --- | | 新 | $200 |
但是,我希望位置2的数据透视表包括所有可能的类别和状态,如果它们不存在,则为0。总之,我希望位置2的数据透视表如下所示:
位置2: | 状态 | 家具 | 办公用品 | | --- | --- | --- | | 新 | $0 | $200 | | 旧 | $0 | $0 |
我已经研究了pivot_table()函数的所有选项,但到目前为止还没有找到任何解决方法。
1个回答

5
您可以在按Location拆分之前创建数据透视表。
对于 pd.pivot_table
  • 使用 index=['Location', 'Status'] 设定索引。
  • 传递参数 dropna=False 以允许所有类别显示所有Location和所有Status,即使为空。(默认情况下隐藏空条目)。
  • 传递参数 fill_value=0 以将NaN值填充为0
然后通过 .loc 在数据透视表中找到Location,如下所示:
df_out = pd.pivot_table(df, 
                        values='Price', 
                        index=['Location', 'Status'], 
                        columns='Category', 
                        aggfunc=np.sum, 
                        dropna=False, 
                        fill_value=0)

结果:

print(df_out)

Category         Furniture  Office Supplies
Location Status                            
1        New           200              300
         Old            50              150
2        New             0              200
         Old             0                0

接下来,要仅获取 Location 2 的数据透视表,您可以使用 .loc,如下所示:

df2 = df_out.loc[2]

输出:

print(df2)

Category  Furniture  Office Supplies
Status                              
New               0              200
Old               0                0

编辑(用于添加总计和小计)

如果您还想包括总计(所有位置)和小计(每个位置),您也可以这样做:

对于pd.pivot_table

  • 传递参数margins=Truemargins_name='Total'来设置Location所有数据的Total(总计)边距。
  • pd.pivot_table之后链接命令fillna(0, downcast='infer')。这是为了处理pd.pivot_table故障/错误,即使指定了fill_value=0参数,空条目(例如此情况下的Location=2Status='Old')的边距总计仍将显示NaN
df_out = pd.pivot_table(df, 
                        values='Price', 
                        index=['Location', 'Status'], 
                        columns='Category', 
                        aggfunc=np.sum, 
                        dropna=False, 
                        fill_value=0,
                        margins=True,
                        margins_name='Total'
                        ).fillna(0, downcast='infer')

结果

print(df_out)

Category         Furniture  Office Supplies  Total
Location Status                                   
1        New           200              300    500
         Old            50              150    200
2        New             0              200    200
         Old             0                0      0
Total                  250              650    900

然后,为了添加每个位置小计,我们进一步使用:

(pd.concat([df_out, 
            df_out.query('Location != "Total"')
                  .groupby(level=0).sum()
                  .assign(Status='Sub-total')
                  .set_index('Status', append=True)])
   .sort_index())

结果:

Category            Furniture  Office Supplies  Total
Location Status                                      
1        New              200              300    500
         Old               50              150    200
         Sub-total        250              450    700
2        New                0              200    200
         Old                0                0      0
         Sub-total          0              200    200
Total                     250              650    900

这个回答已经解决了我的主要问题,非常感谢。不过,我在应用 pivot_table() 函数之前进行筛选的原因是我想在边距中包括总计。如果不进行筛选就应用 pivot table 会使一切变得更简单,但现在我没有每个位置的总计了。有什么办法可以解决吗? - Illusionista787
2
@EkaraiGlass 哦,我明白了。您还在数据透视表中设置了边距。对于小计,建议您查看此帖子,也许还有这个帖子 - SeaBean
1
第一篇帖子完美地解决了这个问题。 - Illusionista787
2
@EkaraiGlass 是的,这是一个非常好的解决方案。我也只是要编辑我的解决方案,以包括这一部分。 - SeaBean
2
@EkaraiGlass 我也根据我给你的参考帖子为你定制了添加小计的代码。你可以看一下。 :-) - SeaBean
1
感谢@SeaBean提供如此优雅的解决方案。 - Illusionista787

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接