Python 更有效/干净的数据聚合方式

Python 更有效/干净的数据聚合方式,python,pandas,group-by,pivot-table,Python,Pandas,Group By,Pivot Table,python 3.7.10 熊猫1.1.5 假设我们有一个Dataframe,其中两列包含类别,第三列包含数字。 任务是按第一类分组,然后按第二类分组,并计算总数和份额 import pandas as pd df = pd.DataFrame({ 'fruit': ['orange', 'orange', 'orange', 'banana', 'banana', 'banana'], 'origin': ['USA', 'Canada', 'USA', 'Canada',

python 3.7.10 熊猫1.1.5

假设我们有一个Dataframe,其中两列包含类别,第三列包含数字。 任务是按第一类分组,然后按第二类分组,并计算总数和份额

import pandas as pd

df = pd.DataFrame({
    'fruit': ['orange', 'orange', 'orange', 'banana', 'banana', 'banana'],
    'origin': ['USA', 'Canada', 'USA', 'Canada', 'USA', 'Canada'],
    'weight': [1, 2, 3, 4, 5, 6]
})
df
水果 起源 重量 0 橙色 美国 1. 1. 橙色 加拿大 2. 2. 橙色 美国 3. 3. 香蕉 加拿大 4. 4. 香蕉 美国 5. 5. 香蕉 加拿大 6.
您可以使用两个独立的
groupby
语句使其更简洁:

In [101]: x = df.groupby(['fruit', 'origin']).sum().reset_index()
In [104]: x['share'] = x.groupby('fruit')['weight'].apply(lambda i: i/i.sum())

In [105]: x
Out[105]: 
    fruit  origin  weight     share
0  banana  Canada      10  0.666667
1  banana     USA       5  0.333333
2  orange  Canada       2  0.333333
3  orange     USA       4  0.666667
,根据@Manakin的评论,避免应用:

In [101]: x = df.groupby(['fruit', 'origin']).sum().reset_index()
In [109]: x['share'] = x['weight'].div(x.groupby('fruit')['weight'].transform('sum'))

In [110]: x
Out[110]: 
    fruit  origin  weight     share
0  banana  Canada      10  0.666667
1  banana     USA       5  0.333333
2  orange  Canada       2  0.333333
3  orange     USA       4  0.666667
这不像R那么“干净”,但可以在一行中完成:

df.groupby(['fruit', 'origin'])['weight'].sum().reset_index()\
  .pipe(lambda x: x.assign(share=x['weight'] / 
                                 x.groupby('fruit')['weight'].transform('sum')))
输出:

    fruit  origin  weight     share
0  banana  Canada      10  0.666667
1  banana     USA       5  0.333333
2  orange  Canada       2  0.333333
3  orange     USA       4  0.666667

r
代码直接翻译需要另一个
groupby

>>> ( df.groupby(['fruit', 'origin'])
        .sum().assign(
            share=lambda x: x.groupby('fruit').transform(lambda x: x / x.sum())
         )
     )
               weight     share
fruit  origin                  
banana Canada      10  0.666667
       USA          5  0.333333
orange Canada       2  0.333333
       USA          4  0.666667
或者

或者,可能是最可读的:

>>> ( df.groupby(['fruit', 'origin']).sum()
        .assign(share=lambda x: x.div(df.groupby('fruit').sum()))
    )

               weight     share
fruit  origin                  
banana Canada      10  0.666667
       USA          5  0.333333
orange Canada       2  0.333333
       USA          4  0.666667
更妙的是,终于有了一句名言:):


使用
pd.melt
pd.crosstab
,没有groupby的东西:

>>> df2 = df.melt(['fruit', 'origin'], var_name='stats')
>>> pd.crosstab(
        index=[df2['fruit'], df2['origin']], 
        columns=df2['stats'], 
        values=df2['value'], 
        aggfunc=sum
    ).assign(share=lambda x:x/x.sum(level=0))

stats          weight     share
fruit  origin                  
banana Canada      10  0.666667
       USA          5  0.333333
orange Canada       2  0.333333
       USA          4  0.666667

您可以使用
.set_index
然后在此处使用
.div

Out = df.groupby(["fruit", "origin"]).sum()
Out = Out.assign(share=Out.div(df.set_index(["fruit", "origin"]).sum(level=0)))

               weight     share
fruit  origin                  
banana Canada      10  0.666667
       USA          5  0.333333
orange Canada       2  0.333333
       USA          4  0.666667

在R代码中,您将
sum(weight)
转换为
“total”
,您可以通过将关键字参数传递到
groupby(…).agg(new_name=(“column_name”,aggfunc)
中来完成此操作。您还可以通过编写一个helper函数来执行规范化,从而获得一些清洁度

def normalize(x):
    return x / x.sum()

out = (df.groupby(["fruit", "origin"])
         .agg(total=("weight", "sum"))
         .assign(
             share=lambda df: df.groupby("fruit").transform(normalize)
         ))

print(out)
               total     share
fruit  origin
banana Canada     10  0.666667
       USA         5  0.333333
orange Canada      2  0.333333
       USA         4  0.666667

pandas.pivot_表是解决这个问题的一种方法吗?看到了吗?>@WillianVieira考虑过这个问题。无法给出确切的解决方案。你能提供一个吗?
Out = df.groupby(["fruit", "origin"]).sum()
Out = Out.assign(share=Out.div(df.set_index(["fruit", "origin"]).sum(level=0)))

               weight     share
fruit  origin                  
banana Canada      10  0.666667
       USA          5  0.333333
orange Canada       2  0.333333
       USA          4  0.666667
def normalize(x):
    return x / x.sum()

out = (df.groupby(["fruit", "origin"])
         .agg(total=("weight", "sum"))
         .assign(
             share=lambda df: df.groupby("fruit").transform(normalize)
         ))

print(out)
               total     share
fruit  origin
banana Canada     10  0.666667
       USA         5  0.333333
orange Canada      2  0.333333
       USA         4  0.666667