Python pandas 按多列分组聚合,然后透视

51

在Python中,我有一个类似于以下的pandas DataFrame:

Item | shop1 | shop2 | shop3 | Category
------------------------------------
Shoes| 45    | 50    | 53    | Clothes
TV   | 200   | 300   | 250   | Technology
Book | 20    | 17    | 21    | Books
phone| 300   | 350   | 400   | Technology

假设shop1、shop2和shop3是不同商店中每件物品的成本。现在,在进行一些数据清理后,我需要返回一个类似于以下DataFrame的结果:

Category (index)| size| sum| mean | std
----------------------------------------

其中size代表每个类别中的项目数量,sum、mean和std与应用于三个商店的相同函数相关。如何使用分割-应用-组合模式(groupby、aggregate、apply等)执行这些操作?

有人可以帮我吗?我对这个问题感到疯狂...谢谢!

4个回答

41
df.groupby('Category').agg({'Item':'size','shop1':['sum','mean','std'],'shop2':['sum','mean','std'],'shop3':['sum','mean','std']})

或者如果你想在所有商店中使用它:

df1 = df.set_index(['Item','Category']).stack().reset_index().rename(columns={'level_2':'Shops',0:'costs'})
df1.groupby('Category').agg({'Item':'size','costs':['sum','mean','std']})

34

考虑到在分组聚合中使用字典已被弃用,因此对于Pandas 0.22+进行了编辑。

我们设置了一个非常类似的字典,其中使用字典的键来指定我们的函数,而字典本身用于重命名列。

rnm_cols = dict(size='Size', sum='Sum', mean='Mean', std='Std')
df.set_index(['Category', 'Item']).stack().groupby('Category') \
  .agg(rnm_cols.keys()).rename(columns=rnm_cols)

            Size   Sum        Mean        Std
Category                                     
Books          3    58   19.333333   2.081666
Clothes        3   148   49.333333   4.041452
Technology     6  1800  300.000000  70.710678

选项1
使用agg ← 链接到文档

agg_funcs = dict(Size='size', Sum='sum', Mean='mean', Std='std')
df.set_index(['Category', 'Item']).stack().groupby(level=0).agg(agg_funcs)

                  Std   Sum        Mean  Size
Category                                     
Books        2.081666    58   19.333333     3
Clothes      4.041452   148   49.333333     3
Technology  70.710678  1800  300.000000     6

选项2
更多的价值
使用describe ← 链接至文档

df.set_index(['Category', 'Item']).stack().groupby(level=0).describe().unstack()

            count        mean        std    min    25%    50%    75%    max
Category                                                                   
Books         3.0   19.333333   2.081666   17.0   18.5   20.0   20.5   21.0
Clothes       3.0   49.333333   4.041452   45.0   47.5   50.0   51.5   53.0
Technology    6.0  300.000000  70.710678  200.0  262.5  300.0  337.5  400.0

谢谢你的回答@piRSquared,如果我们想要对同一列应用多个函数,那么字典就不适用了。有没有什么方法可以处理这个问题? - CanCeylan
@CanCeylan 这个使用 Pandas Series 上的 groupby 和聚合。对于 DataFrame,它的行为会有所不同。 - piRSquared

3

将来自不同列的信息聚合行

这里有一些使用多级索引的示例(为简单起见)。当然,您可以通过一些字典推导来缩短(shop1-3,mean/std)的冗余输出,但为了简单起见,我跳过了这一步。

这个答案的特殊之处在于,我们同时使用不同列上的聚合操作,例如(“all_shops”,“mean”)['shop1','shop2','shop3']列的所有分组行取平均值。

输入/输出

df:
    Item  shop1  shop2  shop3    Category
0  Shoes     45     50     53     Clothes
1     TV    200    300    250  Technology
2   Book     20     17     21       Books
3  phone    300    350    400  Technology

df_agg:
           general   all_shops             shop1             shop2             shop3            
             count        mean        std   mean        std   mean        std   mean         std
Category                                                                                        
Books          1.0   19.333333   1.699673   20.0        NaN   17.0        NaN   21.0         NaN
Clothes        1.0   49.333333   3.299832   45.0        NaN   50.0        NaN   53.0         NaN
Technology     2.0  300.000000  64.549722  250.0  70.710678  325.0  35.355339  325.0  106.066017

代码

import numpy as np
import pandas as pd


if __name__ == "__main__":
    pd.set_option('display.max_rows', 500)
    pd.set_option('display.max_columns', 500)
    pd.set_option('display.width', 1000)

    df = pd.DataFrame([
        ["Shoes", 45, 50, 53, "Clothes"],
        ["TV", 200, 300, 250, "Technology"],
        ["Book", 20, 17, 21, "Books"],
        ["phone", 300, 350, 400, "Technology"],
        ], columns=["Item", "shop1", "shop2", "shop3", "Category"]
    )
    print(f"df:\n{df}")

    df_agg = df.groupby("Category").apply(func=lambda df_gr: pd.Series({
        ("general", "count"): len(df_gr),
        ("all_shops", "mean"): df_gr[['shop1', 'shop2', 'shop3']].mean().mean(),
        ("all_shops", "std"): np.std(df_gr[['shop1', 'shop2', 'shop3']].to_numpy()),
        ("shop1", "mean"): df_gr['shop1'].mean(),
        ("shop1", "std"): df_gr['shop1'].std(),
        ("shop2", "mean"): df_gr['shop2'].mean(),
        ("shop2", "std"): df_gr['shop2'].std(),
        ("shop3", "mean"): df_gr['shop3'].mean(),
        ("shop3", "std"): df_gr['shop3'].std(),
    }))
    print(f"\ndf_agg:\n{df_agg}")

2

如果我理解正确,您想为所有商店计算聚合指标,而不是分别为每个商店计算。为此,您可以首先stack您的数据框,然后按Category进行分组:

stacked = df.set_index(['Item', 'Category']).stack().reset_index()
stacked.columns = ['Item', 'Category', 'Shop', 'Price']
stacked.groupby('Category').agg({'Price':['count','sum','mean','std']})

这会导致
           Price                             
           count   sum        mean        std
Category                                     
Books          3    58   19.333333   2.081666
Clothes        3   148   49.333333   4.041452
Technology     6  1800  300.000000  70.710678

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