Convierta el DateTimeIndex de pandas compatible con la zona horaria en una marca de tiempo ingenua, pero en cierta zona horaria

Resuelto joris asked hace 11 años • 9 respuestas

Puede usar la función tz_localizepara hacer que una marca de tiempo o DateTimeIndex tenga en cuenta la zona horaria, pero ¿cómo puede hacer lo contrario: cómo puede convertir una marca de tiempo que tenga en cuenta la zona horaria en una ingenua, preservando al mismo tiempo su zona horaria?

Un ejemplo:

In [82]: t = pd.date_range(start="2013-05-18 12:00:00", periods=10, freq='s', tz="Europe/Brussels")

In [83]: t
Out[83]: 
<class 'pandas.tseries.index.DatetimeIndex'>
[2013-05-18 12:00:00, ..., 2013-05-18 12:00:09]
Length: 10, Freq: S, Timezone: Europe/Brussels

Podría eliminar la zona horaria configurándola en Ninguno, pero luego el resultado se convierte a UTC (las 12 en punto se convirtieron en 10):

In [86]: t.tz = None

In [87]: t
Out[87]: 
<class 'pandas.tseries.index.DatetimeIndex'>
[2013-05-18 10:00:00, ..., 2013-05-18 10:00:09]
Length: 10, Freq: S, Timezone: None

¿Hay otra manera de convertir un DateTimeIndex a una zona horaria ingenua, pero conservando la zona horaria en la que se configuró?


Un poco de contexto sobre el motivo por el que pregunto esto: quiero trabajar con series temporales ingenuas de zonas horarias (para evitar problemas adicionales con las zonas horarias, y no las necesito para el caso en el que estoy trabajando).
Pero por alguna razón, tengo que lidiar con una serie temporal que tiene en cuenta la zona horaria en mi zona horaria local (Europa/Bruselas). Como todos mis demás datos son ingenuos para la zona horaria (pero representados en mi zona horaria local), quiero convertir esta serie temporal en ingenua para seguir trabajando con ella, pero también debe estar representada en mi zona horaria local (así que simplemente elimine la información de la zona horaria). sin convertir la hora visible para el usuario a UTC).

Sé que la hora en realidad se almacena internamente como UTC y solo se convierte a otra zona horaria cuando la representas, por lo que tiene que haber algún tipo de conversión cuando quiero "deslocalizarla". Por ejemplo, con el módulo datetime de Python puedes "eliminar" la zona horaria de esta manera:

In [119]: d = pd.Timestamp("2013-05-18 12:00:00", tz="Europe/Brussels")

In [120]: d
Out[120]: <Timestamp: 2013-05-18 12:00:00+0200 CEST, tz=Europe/Brussels>

In [121]: d.replace(tzinfo=None)
Out[121]: <Timestamp: 2013-05-18 12:00:00> 

Entonces, en base a esto, podría hacer lo siguiente, pero supongo que esto no será muy eficiente cuando trabaje con una serie temporal más grande:

In [124]: t
Out[124]: 
<class 'pandas.tseries.index.DatetimeIndex'>
[2013-05-18 12:00:00, ..., 2013-05-18 12:00:09]
Length: 10, Freq: S, Timezone: Europe/Brussels

In [125]: pd.DatetimeIndex([i.replace(tzinfo=None) for i in t])
Out[125]: 
<class 'pandas.tseries.index.DatetimeIndex'>
[2013-05-18 12:00:00, ..., 2013-05-18 12:00:09]
Length: 10, Freq: None, Timezone: None
joris avatar May 19 '13 03:05 joris
Aceptado

Para responder a mi propia pregunta, mientras tanto, esta funcionalidad se ha agregado a pandas. A partir de pandas 0.15.0 , puede utilizarlo tz_localize(None)para eliminar la zona horaria que da como resultado la hora local.
Vea la entrada de novedades: http://pandas.pydata.org/pandas-docs/stable/whatsnew.html#timezone-handling-improvements

Entonces con mi ejemplo de arriba:

In [4]: t = pd.date_range(start="2013-05-18 12:00:00", periods=2, freq='H',
                          tz= "Europe/Brussels")

In [5]: t
Out[5]: DatetimeIndex(['2013-05-18 12:00:00+02:00', '2013-05-18 13:00:00+02:00'],
                       dtype='datetime64[ns, Europe/Brussels]', freq='H')

El uso tz_localize(None)elimina la información de la zona horaria, lo que da como resultado una hora local ingenua :

In [6]: t.tz_localize(None)
Out[6]: DatetimeIndex(['2013-05-18 12:00:00', '2013-05-18 13:00:00'], 
                      dtype='datetime64[ns]', freq='H')

Además, también puede utilizar tz_convert(None)para eliminar la información de la zona horaria, pero convirtiéndola a UTC, obteniendo así una hora UTC ingenua :

In [7]: t.tz_convert(None)
Out[7]: DatetimeIndex(['2013-05-18 10:00:00', '2013-05-18 11:00:00'], 
                      dtype='datetime64[ns]', freq='H')

Esto es mucho más eficaz que la datetime.replacesolución:

In [31]: t = pd.date_range(start="2013-05-18 12:00:00", periods=10000, freq='H',
                           tz="Europe/Brussels")

In [32]: %timeit t.tz_localize(None)
1000 loops, best of 3: 233 µs per loop

In [33]: %timeit pd.DatetimeIndex([i.replace(tzinfo=None) for i in t])
10 loops, best of 3: 99.7 ms per loop
joris avatar Jan 08 '2016 22:01 joris

Como siempre me cuesta recordar, un breve resumen de lo que hace cada uno de estos:

>>> pd.Timestamp.now()  # naive local time
Timestamp('2019-10-07 10:30:19.428748')

>>> pd.Timestamp.utcnow()  # tz aware UTC
Timestamp('2019-10-07 08:30:19.428748+0000', tz='UTC')

>>> pd.Timestamp.now(tz='Europe/Brussels')  # tz aware local time
Timestamp('2019-10-07 10:30:19.428748+0200', tz='Europe/Brussels')

>>> pd.Timestamp.now(tz='Europe/Brussels').tz_localize(None)  # naive local time
Timestamp('2019-10-07 10:30:19.428748')

>>> pd.Timestamp.now(tz='Europe/Brussels').tz_convert(None)  # naive UTC
Timestamp('2019-10-07 08:30:19.428748')

>>> pd.Timestamp.utcnow().tz_localize(None)  # naive UTC
Timestamp('2019-10-07 08:30:19.428748')

>>> pd.Timestamp.utcnow().tz_convert(None)  # naive UTC
Timestamp('2019-10-07 08:30:19.428748')
Juan A. Navarro avatar Oct 07 '2019 08:10 Juan A. Navarro