Python 更有效/干净的数据聚合方式
python 3.7.10 熊猫1.1.5 假设我们有一个Dataframe,其中两列包含类别,第三列包含数字。 任务是按第一类分组,然后按第二类分组,并计算总数和份额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',
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