Python: Pandas DataFrame groupby resample

Comment analyser des données en les regroupant par les valeurs d'une colonne et en les ré-échantillonant, via une colonne "date", à chaque fin de mois.

J'ai un DataFrame Pandas "df1" avec les données suivantes:

>>> df1
      DATE        USER     DUREE
0     2020-09-03  USER#1   0.50
1     2020-09-02  USER#1   0.00
2     2020-09-02  USER#1   0.25
3     2020-09-01  USER#1   0.00
4     2020-09-01  USER#1   0.25
          ...     ...    ...
10715 2017-08-01  USER#2   0.75
10716 2017-07-19  USER#2   0.00
10717 2017-07-19  USER#2   0.00
10718 2017-07-17  USER#2   0.25
10719 2017-07-17  USER#2   0.00
[10720 rows x 3 columns]

J'ai donc 3 colonnes, une colonne "DATE", une colonne "USER" et une colonne "DUREE" avec les formats suivants:

>>> df1.dtypes
DATE     datetime64[ns]
USER             object
DUREE           float64
dtype: object

J'aimerais donc connaitre le cumul du temps passé par chaque "USER" à chaque fin de mois et obtenir le résultat suivant:

USER        USER#1  USER#2
DATE                     
2011-04-30   76.25    0.00
2011-05-31  109.75    0.00
2011-06-30   74.00    0.00
2011-07-31   31.25    0.00
2011-08-31   83.75    0.00
            ...     ...
2020-05-31    8.75   17.50
2020-06-30   25.50   18.25
2020-07-31    4.25   38.00
2020-08-31   92.75    5.25
2020-09-30    1.25    4.50
[114 rows x 2 columns]

Ce résultat peut-être obtenu en utilisant tout simplement 5 méthodes disponibles pour les DataFrame.

Premièrement, je ré-index mon DataFrame en utilisant la colonne "DATE" - Indispensable pour le ré-échantillonage

>>> df1.set_index('DATE')
              USER  DUREE
DATE                    
2020-09-03  USER#1   0.50
2020-09-02  USER#1   0.00
2020-09-02  USER#1   0.25
2020-09-01  USER#1   0.00
2020-09-01  USER#1   0.25
            ...    ...
2017-08-01  USER#2   0.75
2017-07-19  USER#2   0.00
2017-07-19  USER#2   0.00
2017-07-17  USER#2   0.25
2017-07-17  USER#2   0.00
[10720 rows x 2 columns]

Ensuite, j'effectue mon regroupement sur la colonne "USER"

>>> df1.set_index('DATE').groupby('USER')
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x00000151789EDEB0>

J'obtiens donc un objet "DataFrameGroupBy"

Pour le ré-échantillonage, j'utilise la méthode "resample" qui va agir sur les données contenues dans mon index (par défaut).
Le paramètre "M" va ré-échantilloner mes dates à chaque fin de mois.

>>> df1.set_index('DATE').groupby('USER').resample('M')
<pandas.core.resample.DatetimeIndexResamplerGroupby object at 0x000001517888F3A0>

J'obtiens donc un objet "DatetimeIndexResamplerGroupby"

Je fais maintenant la somme des valeurs de ma colonne "DUREE"

>>> df1.set_index('DATE').groupby('USER').resample('M')['DUREE'].sum()
USER    DATE     
USER#1  2011-04-30     76.25
        2011-05-31    109.75
        2011-06-30     74.00
        2011-07-31     31.25
        2011-08-31     83.75
                       ... 
USER#2  2020-05-31     17.50
        2020-06-30     18.25
        2020-07-31     38.00
        2020-08-31      5.25
        2020-09-30      4.50
Name: DUREE, Length: 153, dtype: float64

Pour finir, je souhaite transposer le premier niveau "USER" de mon index en colonne.
Pour cela j'utilise la méthode "unstack" avec le paramètre "level=0" pour lui indiquer le premier niveau de mon index et je lui demande de remplacer les valeurs nulles par "0" grâce à "fill_value=0"

>>> df1.set_index('DATE').groupby('USER').resample('M')['DUREE'].sum().unstack(level=0, fill_value=0)
USER        USER#1  USER#2
DATE                     
2011-04-30   76.25    0.00
2011-05-31  109.75    0.00
2011-06-30   74.00    0.00
2011-07-31   31.25    0.00
2011-08-31   83.75    0.00
            ...     ...
2020-05-31    8.75   17.50
2020-06-30   25.50   18.25
2020-07-31    4.25   38.00
2020-08-31   92.75    5.25
2020-09-30    1.25    4.50
[114 rows x 2 columns]

Et voilà, mes données ont été agrégées par "USER" et ré-échantillonées à chaque fin de mois.

Il est possible de filtrer les données par année

>>> df1.set_index('DATE').groupby('USER').resample('M')['DUREE'].sum().unstack(level=0, fill_value=0).loc['2020']
USER        USER#1  USER#2
DATE                     
2020-01-31   25.50   50.50
2020-02-29   35.25   15.25
2020-03-31   33.25   26.75
2020-04-30    6.50    1.25
2020-05-31    8.75   17.50
2020-06-30   25.50   18.25
2020-07-31    4.25   38.00
2020-08-31   92.75    5.25
2020-09-30    1.25    4.50

Ou de calculer le total par "USER"

>>> df1.set_index('DATE').groupby('USER').resample('M')['DUREE'].sum().unstack(level=0, fill_value=0).sum()
USER
USER#1    8219.5
USER#2    2432.5
dtype: float64

Ou de calculer le total par "USER" pour une année précise

>>> df1.set_index('DATE').groupby('USER').resample('M')['DUREE'].sum().unstack(level=0, fill_value=0).loc['2020'].sum()
USER
USER#1    233.00
USER#2    177.25
dtype: float64

Simple comme bonjour.

Merci Pandas