Les window functions avec Pandas
Dans certains cas d'applications, la fonction groupby de pandas permet de regrouper les lignes qui partagent des mêmes valeurs sur une ou plusieurs colonnes, permettant ainsi de faire des aggrégations. Mais comment faut-il faire lorsque l'on souhaite aggréger non pas par valeurs, mais par paquets de lignes ?

Dans certains cas d'applications, la fonction groupby de pandas permet de regrouper les lignes qui partagent des mêmes valeurs sur une ou plusieurs colonnes, permettant ainsi de faire des aggrégations. Mais comment faut-il faire lorsque l'on souhaite aggréger non pas par valeurs, mais par paquets de lignes ? Les window functions ont justement été pensés pour ça.
Pour bien comprendre, chargeons le jeu de données suivant.
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_theme(style="darkgrid")
df = pd.read_csv("https://blent-learning-user-ressources.s3.eu-west-3.amazonaws.com/single_notebooks/data/monthly-sunspots.csv")
df['Month'] = pd.to_datetime(df['Month'])
df.head()
Il s'agit d'une série temporelle où la variable Sunspots évolue selon Month. Une Data Visualization permet en général de mieux comprendre.
plt.figure(figsize=(16, 10))
sns.lineplot(
x="Month",
y="Sunspots",
data=df
)
plt.show()
Ce que l'on voit, c'est une série temporelle qui évolue de 1749 à 1983. Pour améliorer la lisibilité, on souhaite applique une moyenne mobile.
La moyenne mobile est un calcul qui consiste à moyenner chaque observation avec ses n voisins. Par exemple, une moyenne mobile de taille 3 prendra à chaque fois, la ligne concernée plus les 2 dernières observations. Des paquets de 3 observations seront utilisés et moyennés, permettant de lisser la courbe.
Fenêtre par paquets
Appliquons une moyenne mobile de taille 6 : pour chaque observation, la fonction rolling va calculer la moyenne avec les 5 observations précédentes.
À lire aussi : découvrez notre formation MLOps
df['Sunspots_MA6'] = df['Sunspots'].rolling(6).mean()
df['Sunspots_MA6']
⚠️ Avec une fonction rolling, il y aura des valeurs manquantes au début, puisque pandas ne pourra pas réunir les 6 observations pour les premières lignes.
Si maintenant, nous faisons le même graphique que précédemment mais en sélectionnant la colonne de moyenne mobile, nous pouvons voir le résultat lissé.
plt.figure(figsize=(16, 10))
sns.lineplot(
x="Month",
y="Sunspots_MA6",
data=df
)
plt.show()
Dans cet exemple, nous avons utilisé des paquets finis d'observations, c'est-à-dire où l'on connaissait exactement combien d'observations nous allions utiliser pour chaque fenêtre.
Mais dans certains cas, les fenêtres par intervalle de temps vont nous simplifier la tâche !
Fenêtre par intervalle de temps
Une autre fonctionnalité puissante de pandas avec les window functions est sa gestion de la temporalité. Plutôt que de définir un nombre de lignes à considérer pour chaque fenêtre, nous pouvons utiliser à la place des intervalles de temps, utile lorsqu'il n'y a pas le même nombre de lignes pour chaque fenêtre.
Dans cet exemple de données, nous avons deux colonnes.
- La colonne
dateavec un format heure, minute et seconde au moment où la ligne a été généré. - La colonne
price, qui correspond à un prix observé à la date correspondante.
prices = pd.read_csv(
"https://blent-learning-user-ressources.s3.eu-west-3.amazonaws.com/single_notebooks/data/window-function-price.csv",
index_col=0
)
prices['date'] = pd.to_datetime(prices['date'])
prices.sort_values('date').head(n=20)
Supposons maintenant que l'on souhaite calculer la moyenne des prix observés sur exactement un jour d'ancienneté.
- Pour la deuxième ligne (indice 3304064), la moyenne des prix observés avec un jour d'ancienneté est (49 + 61) / 2 = 55.
- Pour la troisième ligne (indice 2395941), la moyenne des prix observés avec un jour d'ancienneté est (61 + 50) / 2 = 55.5 (car 2020-05-10 21:17:01 est postérieur à 2020-05-10 19:10:24 qui correspond à l'ancienneté de 1 jour).
- En revanche, la ligne d'indice 2805469 n'a aucune autre observation qu'elle même avec 1 jour d'ancienneté, donc le prix moyen est égal à 142.
À lire aussi : découvrez notre formation MLOps
Ce calcul, qui peut sembler complexe à réaliser soi-même, est en réalité très facile avec les délimiteurs temporels de pandas.
prices_window = prices.sort_values('date').rolling('1D', on='date').mean()
prices_window.head(n=20)
Dans la fonction rolling, nous définissons deux arguments :
- Le premier est le délimiteur temporel. Avec
1D, l'ancienneté observée est de 1 jour. D'autres valeurs sont également possibles, commeSpour le nombre de secondes,Hle nombres d'heures, etc. Ces alias sont répertoriés ici. - Le paramètre
onpermet ensuite de spécifier sur quelle colonne la window function va être appliquée.
Si l'on regarde maintenant la colonne price, elle correspond bien à la moyenne des prix sur un jour d'ancienneté, comme nous l'avions calculé manuellement.
Maintenant, plus aucune raison de ne pas utiliser la fonction rolling sous pandas. 😉


