6 votes

Pandas DatetimeIndex à partir de MongoDB ISODate

Je rencontre des difficultés à travailler avec les heures/les fuseaux horaires. J'ai des données JSON brutes de la forme

{
  "Date": "28 Sep 2009 00:00:00",
  ....
}

Ces données sont ensuite chargées dans MongoDB, et cette représentation de chaîne de la date est transformée en un objet Date JavaScript. Cette conversion en temps de UTC résulte en la date suivante

{
  "_id": ObjectId("577a788f4439e17afd4e21f7"),
  "Date": ISODate("2009-09-27T23:00:00Z")
}

Il "semble" que la date ait en fait été déplacée d'un jour en avant, je suppose (peut-être à tort) que c'est parce que mon ordinateur est réglé sur le fuseau horaire irlandais.

J'ai ensuite lu ces données depuis MongoDB et je les ai utilisées pour créer un DatetimeIndex pandas

idx =  pd.DatetimeIndex([x['Date'] for x in test_docs], freq='D')

ce qui me donne

enter image description here

ce qui est incorrect car l'heure n'a pas été convertie correctement de l'UTC au temps local. J'ai donc suivi la solution donnée dans cette réponse

idx =  pd.DatetimeIndex([x['Date'] for x in test_docs], freq='D')
idx = idx.tz_localize(tz=tz.tzutc())
idx = idx.tz_convert(tz=tz.tzlocal())
frame = DataFrame(test_docs, index=idx)
frame = frame.drop('Date', 1)

ce qui me donne le bon jour en retour

enter image description here

Je normalise ensuite le DatetimeIndex pour supprimer les heures, me permettant de regrouper toutes les entrées par jour.

frame.groupby(idx).sum()

Cependant, à ce stade, quelque chose d'étrange se produit. Les dates se retrouvent regroupées de la manière suivante

enter image description here

mais cela ne reflète pas les dates dans le cadre

enter image description here

Quelqu'un peut-il m'éclairer sur où je pourrais me tromper?


Réponse à @ptrj

Utilisation explicite de mon fuseau horaire en tant que chaîne

idx =  pd.DatetimeIndex([x['Date'] for x in test_docs], freq='D')
idx = idx.tz_localize(tz=tz.tzutc())
idx = idx.tz_convert(tz='Europe/Dublin')
idx = idx.normalize()
frame = DataFrame(test_docs, index=idx)
...
...
aggregate = frame.groupby(idx).sum()
aggregate.plot()

cela ne fonctionne pas pour moi, cela donne le graphique suivant

enter image description here

Pour une raison quelconque, le groupby ne regroupe pas correctement pour 2014, comme indiqué ci-dessous

enter image description here

Si à la place, j'utilise

idx = idx.tz_convert(tz.gettz('Europe/Dublin'))

J'ai le même problème

Convertir en un objet

idx =  pd.DatetimeIndex([x['Date'] for x in test_docs], freq='D')
idx = idx.tz_localize(tz=tz.tzutc())
idx = idx.tz_convert(tz=tz.tzlocal())
idx = idx.normalize()
frame = DataFrame(test_docs, index=idx)
aggregate = frame.groupby(idx.astype(object)).sum()

Cette approche semble fonctionner correctement pour moi

enter image description here

2voto

ptrj Points 3140

J'ai réussi à reproduire l'erreur avec les données suivantes :

idx0 = pd.date_range('2011-11-11', periods=4)
idx1 = idx0.tz_localize(tz.tzutc())
idx2 = idx1.tz_convert(tz.tzlocal())
df = pd.DataFrame([1, 2, 3, 4])

df.groupby(idx2).sum()
Out[20]: 
                           0
1970-01-01 00:00:00-05:00  9
2011-11-10 19:00:00-05:00  1

C'est un bug profond dans le code de pandas, lié exclusivement à tz.tzlocal(). Il se manifeste également dans :

idx2.tz_localize(None)
Out[27]: 
DatetimeIndex(['2011-11-10 19:00:00', '1970-01-01 00:00:00',
               '1970-01-01 00:00:00', '1970-01-01 00:00:00'],
              dtype='datetime64[ns]', freq='D')

Vous pouvez utiliser l'une des solutions suivantes :

  • utilisez explicitement votre fuseau horaire en tant que chaîne de caractères :

    idx2 = idx1.tz_convert(tz='Europe/Dublin')
    df.groupby(idx2).sum()
    Out[29]: 
                               0
    2011-11-11 00:00:00+00:00  1
    2011-11-12 00:00:00+00:00  2
    2011-11-13 00:00:00+00:00  3
    2011-11-14 00:00:00+00:00  4

    ou si cela ne fonctionne pas :

    idx2 = idx1.tz_convert(tz.gettz('Europe/Dublin'))
  • convertissez-le en objet :

    df.groupby(idx2.astype(object)).sum()
    Out[32]: 
                               0
    2011-11-10 19:00:00-05:00  1
    2011-11-11 19:00:00-05:00  2
    2011-11-12 19:00:00-05:00  3
    2011-11-13 19:00:00-05:00  4

En gros, la conversion en n'importe quoi d'autre qu'un DatetimeIndex avec tz=tz.local() devrait fonctionner.


ÉDITER : Ce bug a été corrigé sur le github de pandas. La correction sera disponible dans la version 0.19 de pandas.

0voto

Philip O'Brien Points 409

J'ai réussi à contourner ce problème pour le moment en changeant mon groupby comme suit

frame.groupby([pd.DatetimeIndex([x.date() for x in frame.index])]).sum()

donc là où j'essayais initialement de groupby

idx =  pd.DatetimeIndex([x['Date'] for x in test_docs], freq='D')
idx = idx.tz_localize(tz=tz.tzutc())
idx = idx.tz_convert(tz=tz.tzlocal())
frame.groupby(idx).sum()

je fais maintenant appel à la méthode date sur chaque élément de l'index avant d'effectuer l'opération groupby.

Je poste ceci comme réponse au cas où personne ne répond, mais j'espère que quelqu'un répondra et expliquera ce qui se passe, car ma 'solution' semble trop bricolée à mon goût.

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