web-dev-qa-db-fra.com

Python - fonctions de roulement pour l'objet GroupBy

J'ai un objet de série chronologique grouped du type <pandas.core.groupby.SeriesGroupBy object at 0x03F1A9F0>. grouped.sum() donne le résultat souhaité mais je ne peux pas faire rouler_sum avec l'objet groupby. Existe-t-il un moyen d'appliquer des fonctions de roulement aux objets groupby? Par exemple:

x = range(0, 6)
id = ['a', 'a', 'a', 'b', 'b', 'b']
df = DataFrame(Zip(id, x), columns = ['id', 'x'])
df.groupby('id').sum()
id    x
a    3
b   12

Cependant, je voudrais avoir quelque chose comme:

  id  x
0  a  0
1  a  1
2  a  3
3  b  3
4  b  7
5  b  12
34
user1642513

Remarque: comme identifié par @kekert, le modèle pandas a été déconseillé. Voir les solutions actuelles dans les réponses ci-dessous.

In [16]: df.groupby('id')['x'].apply(pd.rolling_mean, 2, min_periods=1)
Out[16]: 
0    0.0
1    0.5
2    1.5
3    3.0
4    3.5
5    4.5

In [17]: df.groupby('id')['x'].cumsum()
Out[17]: 
0     0
1     1
2     3
3     3
4     7
5    12
35
Garrett

Pour les Googleurs qui tombent sur cette vieille question:

Concernant le commentaire de @ kekert sur la réponse de @ Garrett pour utiliser le nouveau

df.groupby('id')['x'].rolling(2).mean()

plutôt que le désormais obsolète

df.groupby('id')['x'].apply(pd.rolling_mean, 2, min_periods=1)

curieusement, il semble que la nouvelle approche .rolling (). mean () renvoie une série multi-indexée, indexée d'abord par la colonne group_by puis par l'index. Alors que l'ancienne approche renvoyait simplement une série indexée singulièrement par l'index df d'origine, ce qui est peut-être moins logique, mais rendait très pratique l'ajout de cette série en tant que nouvelle colonne dans la trame de données d'origine.

Je pense donc avoir trouvé une solution qui utilise la nouvelle méthode rolling () et fonctionne toujours de la même manière:

df.groupby('id')['x'].rolling(2).mean().reset_index(0,drop=True)

qui devrait vous donner la série

0    0.0
1    0.5
2    1.5
3    3.0
4    3.5
5    4.5

que vous pouvez ajouter en colonne:

df['x'] = df.groupby('id')['x'].rolling(2).mean().reset_index(0,drop=True)
68
Kevin Wang

Voici un autre moyen qui se généralise bien et utilise la méthode expand de pandas.

Il est très efficace et fonctionne également parfaitement pour calculs de fenêtres déroulantes avec des fenêtres fixes, comme pour les séries chronologiques.

# Import pandas library
import pandas as pd

# Prepare columns
x = range(0, 6)
id = ['a', 'a', 'a', 'b', 'b', 'b']

# Create dataframe from columns above
df = pd.DataFrame({'id':id, 'x':x})

# Calculate rolling sum with infinite window size (i.e. all rows in group) using "expanding"
df['rolling_sum'] = df.groupby('id')['x'].transform(lambda x: x.expanding().sum())

# Output as desired by original poster
print(df)
  id  x  rolling_sum
0  a  0            0
1  a  1            1
2  a  2            3
3  b  3            3
4  b  4            7
5  b  5           12
4
Sean McCarthy

Je ne suis pas sûr de la mécanique, mais cela fonctionne. Remarque, la valeur renvoyée est juste un ndarray. Je pense que vous pouvez appliquer n'importe quelle fonction cumulative ou "glissante" de cette manière et cela devrait avoir le même résultat.

Je l'ai testé avec cumprod, cummax et cummin et ils ont tous retourné un ndarray. Je pense que pandas est assez intelligent pour savoir que ces fonctions renvoient une série et donc la fonction est appliquée comme une transformation plutôt qu'une agrégation.

In [35]: df.groupby('id')['x'].cumsum()
Out[35]:
0     0
1     1
2     3
3     3
4     7
5    12

Edit: j'ai trouvé curieux que cette syntaxe retourne une série:

In [54]: df.groupby('id')['x'].transform('cumsum')
Out[54]:
0     0
1     1
2     3
3     3
4     7
5    12
Name: x
2
Zelazny7