On peut choisir d'indexer son tableau par un index chronologique. Dans ce cas certaines opérations liées au temps deviennent possibles.
Note: regardez ce qu'est une date/heure dans Python si vous ne l'avez pas encore fait : [Introduction à DateTime](../lesson2 Deeper in Python/21 datetime.ipynb)
Création et utilisation d'un tableau chronologique¶
Les dates sont ordonnées et peuvent être données dans une liste de dates, sous forme d’index, ou avec la méthode
pd.date_range (début, fin, période, fréquence)
où vous devez choisir entre fin et période, la période étant le nombre d'itérations
La fréquence est définie par ces codes (attention, ils sont différents de ceux de Numpy datetime) :
B jours ouvrables
C jours ouvrables personnalisée (à titre expérimental)
J jour
W hebdomadaire
M mensuel
BM mois des affaires
CBM mois d'activité personnalisée
MS début de mois
BMS début de mois d'activité
CBMS début de mois d'activité personnalisée
Q fin de trimestre
BQ trimestre d’affaires
QS début de trimestre
BQS début du trimestre d'activité
A fin d'année
BA fin d'année d'exercice
AS début d'année
BAS année de début d'exercice
BH heure professionel
H heure
T, min minute
S seconde
L, ms millisecondes
U, us microsecondes
N nanosecondes
import numpy as np
import pandas as pd
np.random.seed(1)
dates = pd.date_range('2016-08-28', '2016-09-06', freq='B') # begin, end, only business days
dates
DatetimeIndex(['2016-08-29', '2016-08-30', '2016-08-31', '2016-09-01', '2016-09-02', '2016-09-05', '2016-09-06'], dtype='datetime64[ns]', freq='B')
Avec cet index on peut créer un tableau chronologique :
tdf1 = pd.DataFrame({'temperature': 20 + np.random.randint(0,5,7),
'pression' : 1 + np.random.random(7)/10 },
index=dates)
tdf1
temperature | pression | |
---|---|---|
2016-08-29 | 23 | 1.039658 |
2016-08-30 | 24 | 1.038791 |
2016-08-31 | 20 | 1.066975 |
2016-09-01 | 21 | 1.093554 |
2016-09-02 | 23 | 1.084631 |
2016-09-05 | 20 | 1.031327 |
2016-09-06 | 20 | 1.052455 |
Comme pour les tableaux usuels on peut selectionner les parties qui nous intéressent avec loc
et les filtres.
Il est également possible de contraindre les dates :
tdf1.loc['2016-08'] # just August
temperature | pression | |
---|---|---|
2016-08-29 | 23 | 1.039658 |
2016-08-30 | 24 | 1.038791 |
2016-08-31 | 20 | 1.066975 |
tdf1.loc['2016-09-03':] # after that date even if the date is not in the index
temperature | pression | |
---|---|---|
2016-09-05 | 20 | 1.031327 |
2016-09-06 | 20 | 1.052455 |
Manipulation¶
Boucher les trous¶
Soit deux sources d'information incomplètes, utilisons les méthodes que l'on a déjà vu pour boucher les trous.
tdf2 = tdf1.copy()
tdf1.drop(tdf1.index[[0,1,3]], inplace=True) # we remove some data
tdf2.drop(tdf2.index[[5,6]], inplace=True) # more data removed
tdf2.drop(columns='pression', inplace=True)
display(tdf1, tdf2)
temperature | pression | |
---|---|---|
2016-08-31 | 20 | 1.066975 |
2016-09-02 | 23 | 1.084631 |
2016-09-05 | 20 | 1.031327 |
2016-09-06 | 20 | 1.052455 |
temperature | |
---|---|
2016-08-29 | 23 |
2016-08-30 | 24 |
2016-08-31 | 20 |
2016-09-01 | 21 |
2016-09-02 | 23 |
On utilse merge
pour aggréger les données des deux tableaux. Comme on veut se baser sur la température ainsi que sur l'index, il faut passer l'index en une colonne (c'est fusion sur l'index ou sur des colonnes).
res = pd.merge(tdf1.reset_index(), tdf2.reset_index(), on=['temperature', 'index'], how='outer')
display(res)
res.set_index('index').sort_index()
index | temperature | pression | |
---|---|---|---|
0 | 2016-08-31 | 20 | 1.066975 |
1 | 2016-09-05 | 20 | 1.031327 |
2 | 2016-09-06 | 20 | 1.052455 |
3 | 2016-09-01 | 21 | NaN |
4 | 2016-08-29 | 23 | NaN |
5 | 2016-09-02 | 23 | 1.084631 |
6 | 2016-08-30 | 24 | NaN |
temperature | pression | |
---|---|---|
index | ||
2016-08-29 | 23 | NaN |
2016-08-30 | 24 | NaN |
2016-08-31 | 20 | 1.066975 |
2016-09-01 | 21 | NaN |
2016-09-02 | 23 | 1.084631 |
2016-09-05 | 20 | 1.031327 |
2016-09-06 | 20 | 1.052455 |
Si les deux sources de données ne sont pas d'accord sur une valeur, que se passe-t-il ?
tdf1.loc['2016-08-31','temperature'] = 19
res = pd.merge(tdf1.reset_index(), tdf2.reset_index(), on=['temperature', 'index'], how='outer')
res = res.set_index('index').sort_index()
res
temperature | pression | |
---|---|---|
index | ||
2016-08-29 | 23 | NaN |
2016-08-30 | 24 | NaN |
2016-08-31 | 19 | 1.066975 |
2016-08-31 | 20 | NaN |
2016-09-01 | 21 | NaN |
2016-09-02 | 23 | 1.084631 |
2016-09-05 | 20 | 1.031327 |
2016-09-06 | 20 | 1.052455 |
merge
en mode outer
garde toutes les valeurs, aussi a 2 températures différentes pour le 31/08.
Interpolation¶
Comme avec Numpy, l'interpolation peut être faite en prenant en compte les dates et donc l'écart entre 2 dates successives.
res.interpolate(method='time')
temperature | pression | |
---|---|---|
index | ||
2016-08-29 | 23 | NaN |
2016-08-30 | 24 | NaN |
2016-08-31 | 19 | 1.066975 |
2016-08-31 | 20 | 1.066975 |
2016-09-01 | 21 | 1.075803 |
2016-09-02 | 23 | 1.084631 |
2016-09-05 | 20 | 1.031327 |
2016-09-06 | 20 | 1.052455 |
Il est également possible de changer l'index et de demander de recalculer les valeurs sur le nouvel index. Bizarrement cela ne marche que sur une Serie (une colonne) :
tdf1['temperature'].resample('30h').interpolate('time')
2016-08-31 00:00:00 19.00 2016-09-01 06:00:00 19.25 2016-09-02 12:00:00 19.50 2016-09-03 18:00:00 19.75 2016-09-05 00:00:00 20.00 Freq: 30h, Name: temperature, dtype: float64
Pour le faire sur un DataFrame complet on peut faire les colonnes une par une ou cela :
interpol = tdf1.asfreq('30h')
display(interpol)
tmp = pd.concat([tdf1, interpol]).sort_index().interpolate(method='time').drop_duplicates()
tmp.loc[interpol.index]
temperature | pression | |
---|---|---|
2016-08-31 00:00:00 | 19.0 | 1.066975 |
2016-09-01 06:00:00 | NaN | NaN |
2016-09-02 12:00:00 | NaN | NaN |
2016-09-03 18:00:00 | NaN | NaN |
2016-09-05 00:00:00 | 20.0 | 1.031327 |
temperature | pression | |
---|---|---|
2016-08-31 00:00:00 | 19.00 | 1.066975 |
2016-09-01 06:00:00 | 21.50 | 1.078010 |
2016-09-02 12:00:00 | 22.50 | 1.075747 |
2016-09-03 18:00:00 | 21.25 | 1.053537 |
2016-09-05 00:00:00 | 20.00 | 1.031327 |
Grouper les données¶
Lorsqu'une colonne a des date de type datetime
on sent bien qu'il ne va pas être possble de faire un groupby
directement puisque toutes les dates sont en général différente (à la milliseconde au moins).
Aussi il faut grouper les donner suivant un intervale ce qui se fait avec dt.to_period
:
df = pd.DataFrame({'date': pd.date_range(start="2020-01-01", periods=15, freq='5D'),
'day sales': np.random.randint(50,size=15)}).sort_values('date')
df
date | day sales | |
---|---|---|
0 | 2020-01-01 | 42 |
1 | 2020-01-06 | 28 |
2 | 2020-01-11 | 29 |
3 | 2020-01-16 | 14 |
4 | 2020-01-21 | 4 |
5 | 2020-01-26 | 23 |
6 | 2020-01-31 | 23 |
7 | 2020-02-05 | 41 |
8 | 2020-02-10 | 49 |
9 | 2020-02-15 | 30 |
10 | 2020-02-20 | 32 |
11 | 2020-02-25 | 22 |
12 | 2020-03-01 | 13 |
13 | 2020-03-06 | 41 |
14 | 2020-03-11 | 9 |
df.groupby(df['date'].dt.to_period('W'))['day sales'].mean()
date 2019-12-30/2020-01-05 42.0 2020-01-06/2020-01-12 28.5 2020-01-13/2020-01-19 14.0 2020-01-20/2020-01-26 13.5 2020-01-27/2020-02-02 23.0 2020-02-03/2020-02-09 41.0 2020-02-10/2020-02-16 39.5 2020-02-17/2020-02-23 32.0 2020-02-24/2020-03-01 17.5 2020-03-02/2020-03-08 41.0 2020-03-09/2020-03-15 9.0 Freq: W-SUN, Name: day sales, dtype: float64
On voit qu'on a dans date
le début et la fin de la semaine, c'est bien une période. On note que Freq
indique W-Sun
pour souligner que les semaines finissent le dimanche.
Ré-échantillonner avec resample
¶
Il est possible de grouper des données sur une date, le dernier jour de la semaine par exemple, dans ce cas date
reste une date et il faut utiliser resample
avec comme argument
- 'W-Sun' pour grouper toutes les données de la semaine sur le dimanche et avoir le même comportement que ci-dessus. On peut choisir un autre jour de la semaine.
- 'ME' pour regrouper les données à la fin du mois
- 'YE' à la fin de l'année mais on peut aussi choisir le mois : 'Y-Mar' pour avoir une fenêtre glissante de mars en mars par exemple
df.resample('W-Sun', on='date').mean() # without 'on', dates shoud be in the index
day sales | |
---|---|
date | |
2020-01-05 | 42.0 |
2020-01-12 | 28.5 |
2020-01-19 | 14.0 |
2020-01-26 | 13.5 |
2020-02-02 | 23.0 |
2020-02-09 | 41.0 |
2020-02-16 | 39.5 |
2020-02-23 | 32.0 |
2020-03-01 | 17.5 |
2020-03-08 | 41.0 |
2020-03-15 | 9.0 |
Plus¶
Pour plus d'information sur les tableaux chronologiques on regardera la page sur les séries chronologiques : http://pandas.pydata.org/pandas-docs/stable/timeseries.html