Pivot 中每个级别的小计 table

Subtotal for each level in Pivot table

我正在尝试创建一个数据透视表 table,除了总计之外,每个行级别之间还有一个小计。

我创建了我的 df。

import pandas as pd
df = pd.DataFrame(
    np.array([['SOUTH AMERICA', 'BRAZIL', 'SP', 500],
             ['SOUTH AMERICA', 'BRAZIL', 'RJ', 200],
             ['SOUTH AMERICA', 'BRAZIL', 'MG', 150],
             ['SOUTH AMERICA', 'ARGENTINA', 'BA', 180],
             ['SOUTH AMERICA', 'ARGENTINA', 'CO', 300],
             ['EUROPE', 'SPAIN', 'MA', 400],
             ['EUROPE', 'SPAIN', 'BA', 110],
             ['EUROPE', 'FRANCE', 'PA', 320],
             ['EUROPE', 'FRANCE', 'CA', 100],
             ['EUROPE', 'FRANCE', 'LY', 80]], dtype=object),
    columns=["CONTINENT", "COUNTRY","LOCATION","POPULATION"]
)

之后我创建了我的枢轴 table 如下所示

table = pd.pivot_table(df, values=['POPULATION'], index=['CONTINENT', 'COUNTRY', 'LOCATION'], fill_value=0, aggfunc=np.sum, dropna=True)
table

为了做小计我开始求和 CONTINENT level

tab_tots = table.groupby(level='CONTINENT').sum()
tab_tots.index = [tab_tots.index, ['Total'] * len(tab_tots)]

并与我的第一个数据透视表连接起来得到小计。

pd.concat([table, tab_tots]).sort_index()

得到它:

我怎样才能像第一个 table 那样获得按级别分隔的值?

我找不到执行此操作的方法。

你想做这样的事情

tab_tots.index = [tab_tots.index, ['Total'] * len(tab_tots), [''] * len(tab_tots)]

我认为您正在寻找以下内容

In [277]: pd.concat([table, tab_tots]).sort_index()
Out[277]:
                                  POPULATION
CONTINENT     COUNTRY   LOCATION
EUROPE        FRANCE    CA               100
                        LY                80
                        PA               320
              SPAIN     BA               110
                        MA               400
              Total                     1010
SOUTH AMERICA ARGENTINA BA               180
                        CO               300
              BRAZIL    MG               150
                        RJ               200
                        SP               500
              Total                     1330

请注意,虽然这解决了您的问题,但在风格上不是好的编程。您的汇总级别逻辑不一致。

这对于 UI 界面来说是有意义的,但如果您正在使用数据,最好使用

tab_tots.index = [tab_tots.index, ['All'] * len(tab_tots), ['All'] * len(tab_tots)]

这遵循 SQL table 逻辑,会给你

In [289]: pd.concat([table, tab_tots]).sort_index()
Out[289]:
                                  POPULATION
CONTINENT     COUNTRY   LOCATION
EUROPE        All       All             1010
              FRANCE    CA               100
                        LY                80
                        PA               320
              SPAIN     BA               110
                        MA               400
SOUTH AMERICA ARGENTINA BA               180
                        CO               300
              All       All             1330
              BRAZIL    MG               150
                        RJ               200
                        SP               500

有了 margins=True,需要稍微改变一下 pivot indexcolumns

newdf=pd.pivot_table(df, index=['CONTINENT'],values=['POPULATION'], columns=[ 'COUNTRY', 'LOCATION'], aggfunc=np.sum, dropna=True,margins=True)
newdf.drop('All').stack([1,2])
Out[132]: 
                                  POPULATION
CONTINENT     COUNTRY   LOCATION            
EUROPE        All                     1010.0
              FRANCE    CA             100.0
                        LY              80.0
                        PA             320.0
              SPAIN     BA             110.0
                        MA             400.0
SOUTH AMERICA ARGENTINA BA             180.0
                        CO             300.0
              All                     1330.0
              BRAZIL    MG             150.0
                        RJ             200.0
                        SP             500.0

IIUC:

contotal = table.groupby(level=0).sum().assign(COUNTRY='TOTAL', LOCATION='').set_index(['COUNTRY','LOCATION'], append=True)
coutotal = table.groupby(level=[0,1]).sum().assign(LOCATION='TOTAL').set_index(['LOCATION'], append=True)

df_out = (pd.concat([table,contotal,coutotal]).sort_index())
df_out

输出:

                                  POPULATION
CONTINENT     COUNTRY   LOCATION            
EUROPE        FRANCE    CA               100
                        LY                80
                        PA               320
                        TOTAL            500
              SPAIN     BA               110
                        MA               400
                        TOTAL            510
              TOTAL                     1010
SOUTH AMERICA ARGENTINA BA               180
                        CO               300
                        TOTAL            480
              BRAZIL    MG               150
                        RJ               200
                        SP               500
                        TOTAL            850
              TOTAL                     1330