web-dev-qa-db-fra.com

Comment créer une nouvelle colonne à partir de la sortie de pandas groupby (). Sum ()?

Essayer de créer une nouvelle colonne à partir du calcul groupby. Dans le code ci-dessous, j'obtiens les valeurs calculées correctes pour chaque date (voir groupe ci-dessous), mais lorsque j'essaie de créer une nouvelle colonne (df['Data4']) avec cela je reçois NaN. J'essaie donc de créer une nouvelle colonne dans le cadre de données avec la somme de Data3 pour toutes les dates et l’appliquer à chaque rangée de dates. Par exemple, 2015-05-08 est sur 2 lignes (le total est 50 + 5 = 55) et dans cette nouvelle colonne, j'aimerais avoir 55 dans les deux lignes.

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
52
fe ner

Vous voulez utiliser transform Cela retournera une série avec l'index aligné sur le df afin que vous puissiez ensuite l'ajouter comme une nouvelle colonne:

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
149
EdChum

Comment créer une nouvelle colonne avec Groupby (). Sum ()?

Il y a deux manières - l'une directe et l'autre légèrement plus intéressante.


Tout le monde est favori: GroupBy.transform() avec 'sum'

La réponse de @Ed Chum peut être un peu simplifiée. Appelez DataFrame.groupby Plutôt que Series.groupby. Cela se traduit par une syntaxe plus simple.

# 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 

C'est un peu rapide,

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)

Non conventionnel, mais mérite votre considération: GroupBy.sum() + Series.map()

Je suis tombé sur une idiosyncrasie intéressante dans l'API. D'après ce que je dis, vous pouvez reproduire cela sur toutes les versions majeures supérieures à 0.20 (j'ai testé cela sur 0.23 et 0.24). Il semble que vous puissiez constamment gagner quelques millisecondes du temps pris par transform si vous utilisez plutôt une fonction directe de GroupBy et que vous la diffusez à l'aide de 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

Comparer avec

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

Mes tests montrent que map est un peu plus rapide si vous pouvez vous permettre d’utiliser la fonction directe GroupBy (telle que mean, min, max, first, etc). Il est plus ou moins rapide dans la plupart des situations d’environ 200 000 enregistrements. Après cela, la performance dépend vraiment des données.

(Gauche: v0.23, Droite: v0.24)

Belle alternative à connaître, et mieux si vous avez des cadres plus petits avec un plus petit nombre de groupes. . . mais je recommanderais transform comme premier choix. Pensé que cela valait la peine de partager de toute façon.

Code de référence, pour référence:

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
)
16
cs95