web-dev-qa-db-fra.com

Ajouter les dates manquantes à pandas dataframe

Mes données peuvent avoir plusieurs événements à une date donnée ou AUCUN événement à une date. Je prends ces événements, fais un compte par date et 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 plage de 30 dates, par exemple. 09-01-2013 à 30/09/2013 Cependant S ne peut avoir que 25 ou 26 jours car aucun événement ne s'est produit pour une date donnée. J'obtiens ensuite une erreur 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 de s'y attaquer? Est-ce que je veux supprimer des dates sans valeur de IDX ou (ce que je préférerais faire) ajoute à la série la date manquante avec un nombre égal à 0 .Je préfère avoir un graphique complet de 30 jours avec 0 valeurs. Si cette approche est correcte, des suggestions sur la façon de commencer? Ai-je besoin d'une sorte de fonction dynamique reindex?

Voici un extrait de S (df.groupby(['simpleDate']).size()), ne notez aucune entrée pour 04 et 05.

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

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)

les 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
...
194
unutbu

Une solution plus rapide consiste à utiliser .asfreq() . Cela ne nécessite pas la création d'un nouvel index à appeler dans .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
24
Brad Solomon

Un problème est que reindex échouera s'il existe des valeurs en double. Supposons que nous travaillions 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

les 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 date, 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 s’agit d’un dup)

Au lieu de cela, nous pouvons utiliser .loc pour rechercher des entrées pour toutes les dates comprises dans la plage:

df.loc[all_days]

les 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é sur la série de colonnes pour remplir les blancs si nécessaire.

21
Nick Edgar

Une approche alternative est resample , qui peut gérer les dates en double en plus des dates manquantes. Par exemple:

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

resample est une opération différée comme groupby, vous devez donc la suivre avec une autre opération. Dans ce cas, mean fonctionne bien, mais vous pouvez également utiliser de nombreuses autres méthodes pandas telles que max, sum, etc.).

Voici les données d'origine, 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 NaN afin de bien comprendre comment cela fonctionne, mais vous pouvez ajouter fillna(0) pour remplacer les NaN par des zéros comme demandé par l'OP ou bien utiliser quelque chose comme interpolate() pour remplir avec des valeurs non nulles basées sur les lignes voisines.

13
JohnE

Voici une méthode intéressante pour renseigner les dates manquantes dans un cadre de données, avec votre choix de fill_value, days_back à remplir et à trier (date_order) pour trier le cadre 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
4
eiTan LaVi