Как мне создать новый столбец из вывода pandas groupby(). Sum()?

Попытка создать новый столбец из 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

Ответ 1

Вы хотите использовать transform, это вернет серию с индексом, выровненным по отношению к df, чтобы вы могли добавить его как новый колонка:

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

Ответ 2

Как создать новый столбец с помощью Groupby(). Sum()?

Есть два способа - один простой, а другой чуть более интересный.


Любимый всем: GroupBy.transform() с 'sum'

@Ред Чум ответ может быть упрощен, немного. Вызовите 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). Похоже, что вы постоянно можете GroupBy время, затрачиваемое на 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 (например, mean, min, max, first и т.д.). Это более или менее быстрее для большинства общих ситуаций примерно до 200 тысяч записей. После этого производительность действительно зависит от данных.

w66EQm.png4d2pVm.png

(Слева: 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
)