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
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
...
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
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.
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.
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