如何从pandas groupby()。sum()的输出中创建新列?


82

尝试从groupby计算中创建新列。在下面的代码中,我获得了每个日期的正确计算值(请参阅下面的组),但是当我尝试df['Data4']用它创建一个新列()时,我得到了NaN。因此,我正在尝试在数据框中使用Data3所有日期的总和创建一个新列,并将其应用于每个日期行。例如,2015-05-08位于2行中(总计为50 + 5 = 55),在这个新列中,我希望两行都具有55。

import pandas as pd
import numpy as np
from pandas import DataFrame

df = pd.DataFrame({
    'Date' : ['2015-05-08', '2015-05-07', '2015-05-06', '2015-05-05', '2015-05-08', '2015-05-07', '2015-05-06', '2015-05-05'], 
    'Sym'  : ['aapl', 'aapl', 'aapl', 'aapl', 'aaww', 'aaww', 'aaww', 'aaww'], 
    'Data2': [11, 8, 10, 15, 110, 60, 100, 40],
    'Data3': [5, 8, 6, 1, 50, 100, 60, 120]
})

group = df['Data3'].groupby(df['Date']).sum()

df['Data4'] = group

Answers:


189

您要使用transform此方法将返回索引与df对齐的Series,然后可以将其添加为新列:

In [74]:

df = pd.DataFrame({'Date': ['2015-05-08', '2015-05-07', '2015-05-06', '2015-05-05', '2015-05-08', '2015-05-07', '2015-05-06', '2015-05-05'], 'Sym': ['aapl', 'aapl', 'aapl', 'aapl', 'aaww', 'aaww', 'aaww', 'aaww'], 'Data2': [11, 8, 10, 15, 110, 60, 100, 40],'Data3': [5, 8, 6, 1, 50, 100, 60, 120]})
​
df['Data4'] = df['Data3'].groupby(df['Date']).transform('sum')
df
Out[74]:
   Data2  Data3        Date   Sym  Data4
0     11      5  2015-05-08  aapl     55
1      8      8  2015-05-07  aapl    108
2     10      6  2015-05-06  aapl     66
3     15      1  2015-05-05  aapl    121
4    110     50  2015-05-08  aaww     55
5     60    100  2015-05-07  aaww    108
6    100     60  2015-05-06  aaww     66
7     40    120  2015-05-05  aaww    121

如果我们在这里有第二个分组,会发生什么情况:stackoverflow.com/a/40067099/281545
Mr_and_Mrs_D

@Mr_and_Mrs_D,您必须重置索引并在普通列上执行左合并,以重新添加列
EdChum '18

10
另外,也可以使用df.groupby('Date')['Data3'].transform('sum')(我觉得它更容易记住)。
克莱布(Cleb)

39

如何使用Groupby()。Sum()创建新列?

有两种方法-一种简单明了,另一种更有趣。


每个人都喜欢的:GroupBy.transform()'sum'

@Ed Chum的答案可以简化一点。致电DataFrame.groupby而不是Series.groupby。这样可以简化语法。

# The setup.
df[['Date', 'Data3']]

         Date  Data3
0  2015-05-08      5
1  2015-05-07      8
2  2015-05-06      6
3  2015-05-05      1
4  2015-05-08     50
5  2015-05-07    100
6  2015-05-06     60
7  2015-05-05    120

df.groupby('Date')['Data3'].transform('sum')

0     55
1    108
2     66
3    121
4     55
5    108
6     66
7    121
Name: Data3, dtype: int64 

快一点

df2 = pd.concat([df] * 12345)

%timeit df2['Data3'].groupby(df['Date']).transform('sum')
%timeit df2.groupby('Date')['Data3'].transform('sum')

10.4 ms ± 367 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
8.58 ms ± 559 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

非常规,但值得您考虑:GroupBy.sum()+Series.map()

我偶然发现了API中的一个有趣的特质。据我所知,您可以在0.20以上的任何主要版本上重现它(我在0.23和0.24上进行了测试)。transform如果您改为使用的直接功能GroupBy并使用进行广播,则似乎可以始终节省几毫秒的时间map

df.Date.map(df.groupby('Date')['Data3'].sum())

0     55
1    108
2     66
3    121
4     55
5    108
6     66
7    121
Name: Date, dtype: int64

与之比较

df.groupby('Date')['Data3'].transform('sum')

0     55
1    108
2     66
3    121
4     55
5    108
6     66
7    121
Name: Data3, dtype: int64

我的测试表明,map有点更快,如果你能负担得起使用直接GroupBy函数(如meanminmaxfirst,等)。在大多数情况下,大约20万条记录或多或少会更快。在那之后,性能实际上取决于数据。

(左:v0.23,右:v0.24)

更好的选择,如果您的框架较小且组数较少,则更好。。。但我建议您将其transform作为首选。认为这还是值得分享的。

基准测试代码,以供参考:

import perfplot

perfplot.show(
    setup=lambda n: pd.DataFrame({'A': np.random.choice(n//10, n), 'B': np.ones(n)}),
    kernels=[
        lambda df: df.groupby('A')['B'].transform('sum'),
        lambda df:  df.A.map(df.groupby('A')['B'].sum()),
    ],
    labels=['GroupBy.transform', 'GroupBy.sum + map'],
    n_range=[2**k for k in range(5, 20)],
    xlabel='N',
    logy=True,
    logx=True
)

1
很高兴知道!您介意(至少将来会包含)版本号吗?性能上的差异很有趣,但是这些毕竟是实现细节,将来可能会解决。特别是如果开发人员注意到您的帖子。
jpp

@jpp是的,很公平!添加了版本。此版本已在0.23上进行了测试,但我相信只要您的版本超过0.20,就可以看到差异。
cs95
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.