web-dev-qa-db-fra.com

Changer le fuseau horaire de la colonne date-heure dans les pandas et ajouter un index hiérarchique

J'ai des données avec un horodatage en UTC. J'aimerais convertir le fuseau horaire de cet horodatage en "US/Pacifique" et l'ajouter à l'index hiérarchique d'un fichier DataFrame de pandas. J'ai pu convertir l'horodatage en tant qu'index, mais le formatage du fuseau horaire est perdu lorsque je tente de l'ajouter au DataFrame, sous forme de colonne ou d'index.

>>> import pandas as pd
>>> dat = pd.DataFrame({'label':['a', 'a', 'a', 'b', 'b', 'b'], 'datetime':['2011-07-19 07:00:00', '2011-07-19 08:00:00', '2011-07-19 09:00:00', '2011-07-19 07:00:00', '2011-07-19 08:00:00', '2011-07-19 09:00:00'], 'value':range(6)})
>>> dat.dtypes
#datetime    object
#label       object
#value        int64
#dtype: object

Maintenant, si j'essaie de convertir la série directement, je rencontre une erreur.

>>> times = pd.to_datetime(dat['datetime'])
>>> times.tz_localize('UTC')
#Traceback (most recent call last):
#  File "<stdin>", line 1, in <module>
#  File "/Users/erikshilts/workspace/schedule-detection/python/pysched/env/lib/python2.7/site-packages/pandas/core/series.py", line 3170, in tz_localize
#    raise Exception('Cannot tz-localize non-time series')
#Exception: Cannot tz-localize non-time series

Si je le convertis en index, je peux le manipuler comme une série temporelle. Notez que l'index a maintenant le fuseau horaire Pacifique.

>>> times_index = pd.Index(times)
>>> times_index_pacific = times_index.tz_localize('UTC').tz_convert('US/Pacific')
>>> times_index_pacific
#<class 'pandas.tseries.index.DatetimeIndex'>
#[2011-07-19 00:00:00, ..., 2011-07-19 02:00:00]
#Length: 6, Freq: None, Timezone: US/Pacific

Cependant, je rencontre maintenant des problèmes pour réintégrer l'index dans le bloc de données car il perd son format de fuseau horaire:

>>> dat_index = dat.set_index([dat['label'], times_index_pacific])
>>> dat_index
#                                      datetime label  value
#label                                                      
#a     2011-07-19 07:00:00  2011-07-19 07:00:00     a      0
#      2011-07-19 08:00:00  2011-07-19 08:00:00     a      1
#      2011-07-19 09:00:00  2011-07-19 09:00:00     a      2
#b     2011-07-19 07:00:00  2011-07-19 07:00:00     b      3
#      2011-07-19 08:00:00  2011-07-19 08:00:00     b      4
#      2011-07-19 09:00:00  2011-07-19 09:00:00     b      5

Vous remarquerez que l'index est de nouveau sur le fuseau horaire UTC au lieu du fuseau horaire Pacifique converti.

Comment puis-je modifier le fuseau horaire et l'ajouter en tant qu'index à un DataFrame?

26
Erik Shilts

A présent, cela a été corrigé. Par exemple, vous pouvez maintenant appeler:

dataframe.tz_localize('UTC', level=0)

Vous devrez cependant l'appeler deux fois pour l'exemple donné. (À savoir, une fois pour chaque niveau.)

9
mweerden

Si vous le définissez comme index, il est automatiquement converti en index:

In [11]: dat.index = pd.to_datetime(dat.pop('datetime'), utc=True)

In [12]: dat
Out[12]:
                    label  value
datetime
2011-07-19 07:00:00     a      0
2011-07-19 08:00:00     a      1
2011-07-19 09:00:00     a      2
2011-07-19 07:00:00     b      3
2011-07-19 08:00:00     b      4
2011-07-19 09:00:00     b      5

Puis faites le tz_localize:

In [12]: dat.index = dat.index.tz_localize('UTC').tz_convert('US/Pacific')

In [13]: dat
Out[13]:
                          label  value
datetime
2011-07-19 00:00:00-07:00     a      0
2011-07-19 01:00:00-07:00     a      1
2011-07-19 02:00:00-07:00     a      2
2011-07-19 00:00:00-07:00     b      3
2011-07-19 01:00:00-07:00     b      4
2011-07-19 02:00:00-07:00     b      5

Et ensuite, vous pouvez ajouter la colonne d'étiquette à l'index:

Hmmm c'est vraiment un bug!

In [14]: dat.set_index('label', append=True).swaplevel(0, 1)
Out[14]:
                           value
label datetime
a     2011-07-19 07:00:00      0
      2011-07-19 08:00:00      1
      2011-07-19 09:00:00      2
b     2011-07-19 07:00:00      3
      2011-07-19 08:00:00      4
      2011-07-19 09:00:00      5

Une solution de contournement consiste à convertir le niveau (date/heure) directement (s’il s’agit déjà d’un MultiIndex):

In [15]: dat.index.levels[1] = dat.index.get_level_values(1).tz_localize('UTC').tz_convert('US/Pacific')

In [16]: dat1
Out[16]:
                                 value
label datetime
a     2011-07-19 00:00:00-07:00      0
      2011-07-19 01:00:00-07:00      1
      2011-07-19 02:00:00-07:00      2
b     2011-07-19 00:00:00-07:00      3
      2011-07-19 01:00:00-07:00      4
      2011-07-19 02:00:00-07:00      5
22
Andy Hayden

Une autre solution de contournement qui fonctionne dans les pandas 0.13.1 et qui résout la FrozenList ne peut pas être affectée au problème suivant:

index.levels = pandas.core.base.FrozenList([
    index.levels[0].tz_localize('UTC').tz_convert(tz),
    index.levels[1].tz_localize('UTC').tz_convert(tz)
])

Luttant beaucoup avec ce problème, MultiIndex perd tz dans de nombreuses autres conditions.

1
Mark Horvath

La solution de contournement ne semble pas fonctionner, car les niveaux d'index d'un index hiérarchique semblent immuables (FrozenList est immuable).

Commencer avec un index singulier et ajouter ne fonctionne pas non plus.

La création d'une fonction lambda convertie en horodatage et convertissant chaque membre de la série renvoyée par to_datetime () ne fonctionne pas non plus.

Existe-t-il un moyen de créer une série sensible au fuseau horaire, puis de les insérer dans une image/de les transformer en index? 

joined_event_df = joined_event_df.set_index(['pandasTime'])
joined_event_df.index = joined_event_df.index.get_level_values(1).tz_localize('UTC').tz_convert('US/Central')
# we have tz-awareness above this line
joined_event_df = joined_event_df.set_index('sequence', append = True)
# we lose tz-awareness in the index as soon as we add another index
joined_event_df = joined_event_df.swaplevel(0,1)
0
ivrin