【问题标题】:Pandas adding sum of columns in pivot table (multiindexed)熊猫在数据透视表中添加列总和(多索引)
【发布时间】:2021-04-13 20:36:34
【问题描述】:

我有 df 和 df_pivot 以及以下代码: 将熊猫导入为 pd 将 numpy 导入为 np

df = pd.DataFrame({"A": ["foo", "foo", "foo", "foo", "foo",
                         "bar", "bar", "bar", "bar"],
                  "B": ["one", "one", "one", "two", "two",
                         "one", "one", "two", "two"],
                  "Year": [2019, 2019, 2019, 2019,
                         2019, 2019, 2020, 2020,
                          2020],
                  "Month": ["01", "02", "03", "04", "05", "06", "01", "02", "03"],
                  "Values": [2, 4, 5, 5, 6, 6, 8, 9, 9]})


df_pivot = pd.pivot_table(df, values='Values', index=['A', 'B'],
                    columns=['Year','Month'], aggfunc=np.sum, fill_value=0)

df_pivot 如下所示:

Year    2019                2020      
Month     01 02 03 04 05 06   01 02 03
A   B                                 
bar one    0  0  0  0  0  6    8  0  0
    two    0  0  0  0  0  0    0  9  9
foo one    2  4  5  0  0  0    0  0  0
    two    0  0  0  5  6  0    0  0  0

现在我要做的基本上是在这个 df 中添加三列: 2019财年、2019年初至今、2020年初至今

2019FY 列应该是“2019”下所有值的总和

2019YTD 列应该是定义了期间的“2019”下所有值的总和,即如果期间定义为 04,则 2019YTD 应该对 01/02/03/04 下的 2019 列求和

2020YTD 列应该是“2020”下所有值的总和,

输出表应如下所示:

Year    2019               2019FY 2019YTD 2020      2020YTD
Month     01 02 03 04 05 06                01 02 03
A   B                                 
bar one    0  0  0  0  0  6  6      0      8  0  0      8
    two    0  0  0  0  0  0  0      0      0  9  9      18
foo one    2  4  5  0  0  0 11      11     0  0  0      0
    two    0  0  0  5  6  0 11      5      0  0  0      0

基本上我想知道如何用给定的“月份”对列进行求和,因为从这里我可以自己创建 2019FY/2019YTD/2020YTD,此外,将它们添加到数据透视表的特定插槽中也很重要( 2019年末数据和2020年末数据)。

可行吗?

我到处寻找,但找不到示例。

感谢帮助

谢谢 帕维尔

【问题讨论】:

    标签: python pandas pivot-table


    【解决方案1】:

    对于每一年都可以在自定义函数中创建新列,因此在输出中也是2020FYGroupBy.apply 中的列:

    def f(x):
        #get all months and convert to integers numbers
        c = x.columns.get_level_values(1).astype(int)
        #sum all values
        s1 = x.sum(axis=1)
        #sum 1,2,3,4 months
        s2 = x.loc[:, c <= 4].sum(axis=1)
        x[(f'{x.name}FY','')] = s1
        x[(f'{x.name}YTD','')] = s2
    
        return x
    
    df = df_pivot.groupby(level=0, axis=1, group_keys=False).apply(f)
    print (df)
    Year    2019                2019FY 2019YTD 2020       2020FY 2020YTD
    Month     01 02 03 04 05 06                  01 02 03               
    A   B                                                               
    bar one    0  0  0  0  0  6      6       0    8  0  0      8       8
        two    0  0  0  0  0  0      0       0    0  9  9     18      18
    foo one    2  4  5  0  0  0     11      11    0  0  0      0       0
        two    0  0  0  5  6  0     11       5    0  0  0      0       0
    

    如果需要删除列,请使用tuples,因为MultiIndex

    df = df.drop([('2020FY','')], axis=1)
    print (df)
    Year    2019                2019FY 2019YTD 2020       2020YTD
    Month     01 02 03 04 05 06                  01 02 03        
    A   B                                                        
    bar one    0  0  0  0  0  6      6       0    8  0  0       8
        two    0  0  0  0  0  0      0       0    0  9  9      18
    foo one    2  4  5  0  0  0     11      11    0  0  0       0
        two    0  0  0  5  6  0     11       5    0  0  0       0
        
        
    

    【讨论】:

    • 亲爱的 jezrael,我尝试运行您的代码,但在 print(df) 后我没有收到相同的结果 我收到了我自己的 df,没有 FY/YTD 的其他列,你能告诉我吗?谢谢帕维尔
    • @PawełPoprawski - 所以这意味着有一个月不像4?因为s1 是每年对所有行求和,所以s2 是前 4 列的总和。
    • 我使用了我的初始 df,我认为我应该收到与您相同的结果。您是否在我原来的 df 表中更改了几个月的格式?
    • @PawełPoprawski - 不,但我使用 pandas 1.2.3,你的版本不一样?
    • 我更新了我的熊猫,现在它可以工作了。非常感谢!
    【解决方案2】:
    import pandas as pd
    import numpy as np
    
    df = pd.DataFrame({"A": ["foo", "foo", "foo", "foo", "foo",
                             "bar", "bar", "bar", "bar"],
                       "B": ["one", "one", "one", "two", "two",
                             "one", "one", "two", "two"],
                       "Year": [2019, 2019, 2019, 2019,
                                2019, 2019, 2020, 2020,
                                2020],
                       "Month": ["01", "02", "03", "04", "05", "06", "01", "02", "03"],
                       "Values": [2, 4, 5, 5, 6, 6, 8, 9, 9]})
    
    print(df)
    
    df_pivot = pd.pivot_table(df, values='Values', index=['A', 'B'],
                              columns=['Year', 'Month'], aggfunc=np.sum, fill_value=0)
    print(df_pivot)
    
    # create the same pivot, but just using the year total
    df_year_pivot = pd.pivot_table(df, values='Values', index=['A', 'B'],
                                   columns=['Year'], aggfunc=np.sum, fill_value=0)
    print(df_year_pivot)
    # since the dataframe you wish to add will have 2 index levels
    # you need to add another level when you join the resulting data
    # and since your new level will be a YTD, I just appended it to the year
    multi_index_tuples = [(x, f'{x}YTD') for x in df_year_pivot.columns]
    
    # now, you are going to add the new index level to the df with the level names the same as your first pivot
    df_year_pivot.columns = pd.MultiIndex.from_tuples(multi_index_tuples, names=['Year', 'Month'])
    
    # happily join on the same index
    total_df = pd.merge(df_pivot, df_year_pivot, how='left', left_index=True, right_index=True)
    print(total_df)
    
    # sort the column index
    total_df = total_df.sort_index(axis=1, level=[0,1])
    print(total_df)
    

    【讨论】:

      【解决方案3】:

      您可以使用:

      df.columns.get_level_values()
      df.index.get_level_values()
      

      分割多索引行和列的语法。我建议将 df 的月份列从字符串“01”更改为整数值,这样可以更轻松地使用 运算符进行切片。 但是,如果您需要坚持使用字符串值的月份列名,那么:

      month_num = 4
      df_pivot["2029YTD"] = df_pivot.loc[:, (df_pivot.columns.get_level_values(0) == 2019) & 
                                         (df_pivot.columns.get_level_values(1).astype(int) <= 4)].sum(axis=1)
      df_pivot["2019FY"] = df_pivot.loc[:, df_pivot.columns.get_level_values(0) == 2019].sum(axis=1)
      df_pivot["2020YTD"] = df_pivot.loc[:, df_pivot.columns.get_level_values(0) == 2020].sum(axis=1)
      

      你最终会得到类似的东西:

          Year    2019    2020    2019YTD          2019FY 2020YTD
      Month   01  02  03  04  05  06  01  02  03          
      A   B                                               
      bar one 0   0   0   0   0   6   8   0   0   0   6   8
          two 0   0   0   0   0   0   0   9   9   0   0   18
      foo one 2   4   5   0   0   0   0   0   0   11  11  0
          two 0   0   0   5   6   0   0   0   0   5   11  0
      

      完成后,您可以使用以下方法调整列位置:

      df_pivot = df_pivot.loc[:, [2019, "2019FY", "2019YTD", 2020, "2020YTD"]]
      

      得到类似的东西:

          Year    2019         2019FY  2019YTD 2020      2020YTD
      Month   01  02  03  04  05  06          01  02  03  
      A   B                                               
      bar one 0   0   0   0   0   6   6   0   8   0   0   8
          two 0   0   0   0   0   0   0   0   0   9   9   18
      foo one 2   4   5   0   0   0   11  11  0   0   0   0
          two 0   0   0   5   6   0   11  5   0   0   0   0
      

      【讨论】:

      • OP需要also it is important to add them in specific slot in the pivot table (at the end of 2019 data and at the end of 2020 data).
      • 嘿,在运行上述代码后格式化列位置应该是一件容易的事,使用相同的逻辑使用类似的东西:df_pivot.loc[:, [2019, "2019FY", "2019YTD", 2020, "2020YTD"]],你会得到预期的结果:)
      • 是的,我现在并排运行它,我没有发现任何问题
      • 好的,谢谢。唯一的问题是从 1990 年到 2020 年有几年,然后使用您的解决方案有点问题。因为有点'manually',不是动态的
      猜你喜欢
      • 2018-07-16
      • 2021-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多