Pandas groupby 使用函数数组应用函数

时间:2021-03-23 10:19:05

标签: python pandas group-by data-science feature-engineering

我有一个这样的数据集(示例目的)

df = pd.DataFrame({
    'Store' : [100, 100, 100, 100, 101, 101, 101, 101],
    'Product' : [5, 3, 10, 1, 3, 11, 2, 5],
    'Category' : ['A', 'B', 'C', 'A', 'B', 'A', 'C', 'A'],
    'Sales' : [100, 235, 120, 56, 789, 230, 300, 35]
})

原来是这样

Store   Product Category    Sales
100      5       A           100
100      3       B           235
100      10      C           120
100      1       A           56
101      3       B           789
101      11      A           230
101      2       C           300
101      5       A           35

每个商店都有一些产品,每个产品都有一些类别。我需要找出每家商店的总销售额以及每家商店中每个类别的销售额百分比。所以结果必须是这样的:

         total_Sales    Category_A  Category_B  Category_C
Store               
100       511            30.528376   45.988258   23.483366
101       1354           19.571640   58.271787   22.156573

(类别列以 % 为单位)

目前我是这样做的:

df1 = df.groupby(['Store']).apply(lambda x: x['Sales'].sum())
df1 = df1.to_frame()
df1 = df1.rename(columns={0 : 'Sales'})

def category_util(x, col, cat):
    total_sales = x['Sales'].sum()
    cat_sales = x[x[col] == cat]['Sales'].sum()
    if cat_sales == 0:
        return 0
    else:
        return cat_sales*100/total_sales
df1['Category_A'] = df.groupby(['Store']).apply(lambda x: category_util(x, 'Category', 'A'))
df1['Category_B'] = df.groupby(['Store']).apply(lambda x: category_util(x, 'Category', 'B'))
df1['Category_C'] = df.groupby(['Store']).apply(lambda x: category_util(x, 'Category', 'C'))

df1 是所需的输出。它工作正常,但每个 apply 函数都一次又一次地对分组列进行排序,对于大型数据集,非常耗时。我想在一个函数调用中做到这一点。我试过类似的东西:

df.groupby(['Store']).agg([lambda x: category_util(x, 'Category', 'A'),
                          lambda x: category_util(x, 'Category', 'B'),
                          lambda x: category_util(x, 'Category', 'C')])

但它失败了 KeyError 为“销售”

pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_value()

pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_value()

pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()

pandas/_libs/index_class_helper.pxi in pandas._libs.index.Int64Engine._check_type()

KeyError: 'Sales'

有什么解决办法吗?有没有办法将 apply 函数与一组 lambda 函数一起使用并一次性计算所有列?如果用 apply 不行,用 agg 可以吗?这真的会为我节省很多时间。提前致谢。

3 个答案:

答案 0 :(得分:2)

您可以使用 pivot_tableunstack

table = df.pivot_table(index=['Store', 'Category'], values=['Sales'], aggfunc='sum')#.unstack().add_prefix('Category_')
t_sales = table.sum(level=0)
table=table.div(table.sum(level=0)).mul(100).unstack().add_prefix('Category_')
table.assign(total_sales=t_sales).reset_index()

    Store   Category_Sales                          total_sales
Category    Category_A  Category_B  Category_C  
0   100     30.528376   45.988258   23.483366       511
1   101     19.571640   58.271787   22.156573       1354

答案 1 :(得分:2)

我们可以将 groupbyunstack 一起使用。然后我们将总和除以 axis=1

dfn = df.groupby(['Store', 'Category'])['Sales'].sum().unstack(level=1)
total_sales = dfn.sum(axis=1)
dfn = (
    dfn.div(total_sales, axis=0)
    .mul(100)
    .add_prefix("Category_")
    .assign(total_sales=total_sales)
).rename_axis(columns=None)
       Category_A  Category_B  Category_C  total_sales
Store                                                 
100     30.528376   45.988258   23.483366          511
101     19.571640   58.271787   22.156573         1354

答案 2 :(得分:2)

我们可以创建两个 groupby 对象(相对便宜的操作),并通过管道返回一个包含总和和百分比的数据帧的函数:

group1 = df.groupby('Store')

group2 = df.groupby(['Store', 'Category'])

(df.assign(total_sales = group1.Sales.transform('sum'))
.groupby(['Store','Category'])
.pipe(lambda df: pd.DataFrame({"res" :df.Sales.sum()
                                        .div(df.total_sales.max())
                                        .mul(100), 
                               "total_sales": df.total_sales.max()}))
.set_index('total_sales', append = True)
.unstack('Category')
.droplevel(0, axis=1)
.add_prefix('Category_')
.rename_axis(columns=None)
.reset_index()
)


   Store  total_sales  Category_A  Category_B  Category_C
0    100          511   30.528376   45.988258   23.483366
1    101         1354   19.571640   58.271787   22.156573