234 votes

Ajouter les dates manquantes aux dataframes pandas

Mes données peuvent comporter plusieurs événements à une date donnée ou AUCUN événement à une date donnée. Je prends ces événements, j'obtiens un décompte par date et je les trace. Cependant, lorsque je les trace, mes deux séries ne correspondent pas toujours.

idx = pd.date_range(df['simpleDate'].min(), df['simpleDate'].max())
s = df.groupby(['simpleDate']).size()

Dans le code ci-dessus idx devient une fourchette de 30 dates par exemple. 09-01-2013 à 09-30-2013 Cependant S peut n'avoir que 25 ou 26 jours parce qu'aucun événement ne s'est produit à une date donnée. J'obtiens alors une erreur d'assertion (AssertionError) car les tailles ne correspondent pas lorsque j'essaie de tracer :

fig, ax = plt.subplots()    
ax.bar(idx.to_pydatetime(), s, color='green')

Quelle est la bonne façon d'aborder ce problème ? Est-ce que je veux supprimer les dates sans valeurs de IDX ou (ce que je préférerais) ajouter à la série la date manquante avec un compte de 0. Je préférerais avoir un graphique complet de 30 jours avec 0 valeurs. Si cette approche est la bonne, avez-vous des suggestions sur la façon de commencer ? Ai-je besoin d'une sorte de reindex fonction ?

Voici un extrait de S ( df.groupby(['simpleDate']).size() ), vous remarquerez qu'il n'y a pas d'entrées pour 04 et 05.

09-02-2013     2
09-03-2013    10
09-06-2013     5
09-07-2013     1

405voto

unutbu Points 222216

Vous pouvez utiliser Series.reindex :

import pandas as pd

idx = pd.date_range('09-01-2013', '09-30-2013')

s = pd.Series({'09-02-2013': 2,
               '09-03-2013': 10,
               '09-06-2013': 5,
               '09-07-2013': 1})
s.index = pd.DatetimeIndex(s.index)

s = s.reindex(idx, fill_value=0)
print(s)

rendements

2013-09-01     0
2013-09-02     2
2013-09-03    10
2013-09-04     0
2013-09-05     0
2013-09-06     5
2013-09-07     1
2013-09-08     0
...

92voto

Brad Solomon Points 11873

Une solution plus rapide consiste à utiliser .asfreq() . Cela ne nécessite pas la création d'un nouvel index à appeler à l'intérieur de .reindex() .

# "broken" (staggered) dates
dates = pd.Index([pd.Timestamp('2012-05-01'), 
                  pd.Timestamp('2012-05-04'), 
                  pd.Timestamp('2012-05-06')])
s = pd.Series([1, 2, 3], dates)

print(s.asfreq('D'))
2012-05-01    1.0
2012-05-02    NaN
2012-05-03    NaN
2012-05-04    2.0
2012-05-05    NaN
2012-05-06    3.0
Freq: D, dtype: float64

38voto

JohnE Points 12788

Une autre approche consiste à resample qui peut traiter les dates en double ainsi que les dates manquantes. En voici un exemple :

df.resample('D').mean()

resample est une opération différée comme groupby Il faut donc la faire suivre d'une autre opération. Dans ce cas, l'opération mean fonctionne bien, mais vous pouvez aussi utiliser d'autres méthodes pandas comme max , sum , etc.

Voici les données originales, mais avec une entrée supplémentaire pour '2013-09-03' :

             val
date           
2013-09-02     2
2013-09-03    10
2013-09-03    20    <- duplicate date added to OP's data
2013-09-06     5
2013-09-07     1

Et voici les résultats :

             val
date            
2013-09-02   2.0
2013-09-03  15.0    <- mean of original values for 2013-09-03
2013-09-04   NaN    <- NaN b/c date not present in orig
2013-09-05   NaN    <- NaN b/c date not present in orig
2013-09-06   5.0
2013-09-07   1.0

J'ai laissé les dates manquantes sous forme de NaNs pour que le fonctionnement soit clair, mais vous pouvez ajouter fillna(0) pour remplacer les NaN par des zéros, comme le demande l'OP, ou bien utiliser quelque chose comme interpolate() à remplir avec des valeurs non nulles en fonction des lignes voisines.

37voto

Nick Edgar Points 101

L'un des problèmes est que reindex échouera s'il y a des valeurs en double. Disons que nous travaillons avec des données horodatées, que nous voulons indexer par date :

df = pd.DataFrame({
    'timestamps': pd.to_datetime(
        ['2016-11-15 1:00','2016-11-16 2:00','2016-11-16 3:00','2016-11-18 4:00']),
    'values':['a','b','c','d']})
df.index = pd.DatetimeIndex(df['timestamps']).floor('D')
df

rendements

            timestamps             values
2016-11-15  "2016-11-15 01:00:00"  a
2016-11-16  "2016-11-16 02:00:00"  b
2016-11-16  "2016-11-16 03:00:00"  c
2016-11-18  "2016-11-18 04:00:00"  d

En raison de la duplication 2016-11-16 une tentative de réindexation :

all_days = pd.date_range(df.index.min(), df.index.max(), freq='D')
df.reindex(all_days)

échoue avec :

...
ValueError: cannot reindex from a duplicate axis

(cela signifie que l'index a des doublons, et non qu'il est lui-même un doublon)

Au lieu de cela, nous pouvons utiliser .loc pour rechercher des entrées pour toutes les dates de l'intervalle :

df.loc[all_days]

rendements

            timestamps             values
2016-11-15  "2016-11-15 01:00:00"  a
2016-11-16  "2016-11-16 02:00:00"  b
2016-11-16  "2016-11-16 03:00:00"  c
2016-11-17  NaN                    NaN
2016-11-18  "2016-11-18 04:00:00"  d

fillna peut être utilisé dans la série de colonnes pour combler les lacunes si nécessaire.

10voto

eiTan LaVi Points 1084

Voici une méthode intéressante pour compléter les dates manquantes dans un cadre de données, avec votre choix de fill_value , days_back à remplir, et l'ordre de tri ( date_order ) en fonction de laquelle trier la base de données :

def fill_in_missing_dates(df, date_col_name = 'date',date_order = 'asc', fill_value = 0, days_back = 30):

    df.set_index(date_col_name,drop=True,inplace=True)
    df.index = pd.DatetimeIndex(df.index)
    d = datetime.now().date()
    d2 = d - timedelta(days = days_back)
    idx = pd.date_range(d2, d, freq = "D")
    df = df.reindex(idx,fill_value=fill_value)
    df[date_col_name] = pd.DatetimeIndex(df.index)

    return df

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X