Convertir los pandas zona horaria-consciente DateTimeIndex ingenuas de marca de hora, pero en cierta zona horaria

Puede utilizar la función tz_localize para hacer una marca de tiempo o DateTimeIndex zona horaria en cuenta, pero, ¿cómo se puede hacer lo contrario: ¿cómo se puede convertir una zona horaria consciente de la hora en un ingenuos, mientras que la preservación de 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

Me podía quitar de la zona horaria se establece en Ninguno, pero luego el resultado se convierte a UTC (12 de la mañana se convirtió 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 forma de convertir un DateTimeIndex a la zona horaria de ingenuo, pero mientras que la preservación de la zona horaria que se establece en?


Algunos contexto sobre la razón por la que estoy pidiendo esto: quiero trabajar con la zona horaria ingenuo unicc (para evitar las complicaciones extra con zonas horarias, y que no necesita de ellos para el caso en el que estoy trabajando).

Pero por alguna razón, tengo que lidiar con una zona horaria-consciente unicc en mi zona horaria local (Europe/Brussels). Como todos mis demás datos de zona horaria de ingenuos (pero representado en mi zona horaria local), quiero convertir este unicc para ingenuo para seguir trabajando con ella, pero también tiene que ser representado en mi zona horaria local (tan sólo hay que quitar la zona horaria de información, sin necesidad de convertir el visibles al usuario hora UTC).

Sé que el tiempo es realmente almacenados internos como la UTC y la convierte en otra zona cuando usted representa, entonces tiene que haber algún tipo de conversión cuando quiero «deslocalizar» de ella. Por ejemplo, con el python datetime módulo se puede «quitar» la zona horaria como este:

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> 

Así que, basándose en esto, yo podría hacer lo siguiente, pero supongo que esto no será muy eficiente cuando se trabaja con un mayor unicc:

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
  • Timezone=None significa UTC… no estoy seguro de entender lo que estamos pidiendo aquí.
  • He añadido un poco de explicación. Quiero mantener el tiempo de ‘ver’ como un usuario. Espero que esto te aclare un poco.
  • Ajá, sí, no me había dado cuenta que se podía hacer eso con replace.
  • Así que en realidad lo que quiero es exactamente la inversa de tz_localize que es lo que el replace(tzinfo=None) para datetimes, pero de hecho no es muy obvio.
InformationsquelleAutor joris | 2013-05-18

7 Kommentare

  1. 88

    Para responder a mi propia pregunta, esta funcionalidad se ha añadido a los pandas en el ínterin. A partir de pandas 0.15.0, puede utilizar tz_localize(None) para eliminar la zona horaria resultante en la hora local.

    Ver el whatsnew entrada: http://pandas.pydata.org/pandas-docs/stable/whatsnew.html#timezone-handling-improvements

    Así que 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')

    utilizando tz_localize(None) elimina la información de la zona horaria resultante en ingenuo hora local:

    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 quitar la información de la zona horaria, pero la conversión a UTC, dando así ingenuo hora UTC:

    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 eficientes de la datetime.replace solució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
    • En caso de que usted está trabajando con algo que ya es la hora UTC y la necesidad de convertir la hora local y la luego de la gota de la zona horaria: from tzlocal import get_localzone, tz_here = get_localzone(), <datetime object>.tz_convert(tz_here).tz_localize(None)
    • Si usted no tiene un índice útil, usted puede necesitar t.dt.tz_localize(None) o t.dt.tz_convert(None). Nota el .dt.
    • Esta solución sólo funciona cuando hay un único tz en la Serie. Si usted tiene múltiples diferentes tz en la misma Serie, luego de ver (y upvote) aquí la solución 🙂 : stackoverflow.com/a/59204751/1054154
  2. 13

    Creo que no se puede lograr lo que usted desea de una manera más eficiente de lo que usted propone.

    El problema subyacente es que las marcas de tiempo (como parece consciente) se compone de dos partes. Los datos que representa la hora UTC, y la zona horaria, tz_info. La zona horaria de información se utiliza únicamente con fines de visualización cuando la impresión de la zona horaria a la de la pantalla. En el tiempo de la pantalla, los datos se compensa adecuadamente y +01:00 (o similar) se agrega a la cadena. Pelar la tz_info valor (con tz_convert(tz=None)) no en realidad no cambiar los datos que representa el ingenuo de parte de la marca de hora.

    Así, la única manera de hacer lo que desea es modificar los datos subyacentes (pandas no permite esto… DatetimeIndex son inmutables, consulte la ayuda en DatetimeIndex), o para crear un nuevo conjunto de timestamp objetos y envolverlos en una nueva DatetimeIndex. La solución no la última:

    pd.DatetimeIndex([i.replace(tzinfo=None) for i in t])

    De referencia, aquí está la replace método de Timestamp (ver tslib.pyx):

    def replace(self, **kwds):
        return Timestamp(datetime.replace(self, **kwds),
                         offset=self.offset)

    Puede referirse a la documentación en datetime.datetime a ver que datetime.datetime.replace también crea un nuevo objeto.

    Si puede, su mejor apuesta por la eficiencia es modificar el código fuente de los datos de modo que (incorrectamente) informes de las marcas de tiempo sin su zona horaria. Usted ha mencionado:

    Quiero trabajar con la zona horaria ingenuo unicc (para evitar las complicaciones extra con zonas horarias, y que no necesita de ellos para el caso en el que estoy trabajando)

    Tengo curiosidad ¿qué complicaciones extra al que se refiere. Yo recomiendo como una regla general para todo el desarrollo de software, mantener su timestamp ‘ingenuo valores’ en UTC. Hay algo peor que mirar dos diferentes int64 valores preguntando en qué zona horaria a la que pertenecen. Si siempre, siempre, siempre uso UTC para el almacenamiento interno, entonces usted va a evitar innumerables dolores de cabeza. Mi mantra es zonas horarias son para humanos I/O sólo.

    • Gracias por la respuesta, y una respuesta tardía: en mi caso no es una aplicación, basta con un análisis científico para mi propio trabajo (así, por ejemplo, ningún intercambio con los colaboradores en el mundo). Y en ese caso, puede ser más fácil trabajar con ingenua de las marcas de tiempo, pero en tu hora local. Así que no tiene que preocuparse acerca de las zonas horarias y sólo puede interpretar la marca de hora como hora local (el extra ‘molestia’ por ejemplo puede ser que todo tiene que ser en zonas horarias, de lo contrario usted obtener cosas como «no se puede comparar desplazamiento de ingenuos y de desplazamiento en cuenta datetimes»). Pero estoy totalmente de acuerdo con usted cuando se trata con aplicaciones más complejas.
  3. 6

    Configuración de la tz atributo del índice explícitamente parece funcionar:

    ts_utc = ts.tz_convert("UTC")
    ts_utc.index.tz = None
    • Tarde el comentario, pero quiero que el resultado sea el tiempo representado en la zona horaria local, no en UTC. Y como muestro en la pregunta, la configuración de la tz a Ninguno que también convierte a UTC.
    • Además, el unicc ya está en la zona horaria consciente, por lo que llamar a tz_convert en él, se producirá un error.
  4. 3

    Edificio en D. A. la sugerencia de que «la única manera de hacer lo que desea es modificar los datos subyacentes» y el uso de numpy para modificar los datos subyacentes…

    Esto funciona para mí, y es bastante rápido:

    def tz_to_naive(datetime_index):
        """Converts a tz-aware DatetimeIndex into a tz-naive DatetimeIndex,
        effectively baking the timezone into the internal representation.
    
        Parameters
        ----------
        datetime_index : pandas.DatetimeIndex, tz-aware
    
        Returns
        -------
        pandas.DatetimeIndex, tz-naive
        """
        # Calculate timezone offset relative to UTC
        timestamp = datetime_index[0]
        tz_offset = (timestamp.replace(tzinfo=None) - 
                     timestamp.tz_convert('UTC').replace(tzinfo=None))
        tz_offset_td64 = np.timedelta64(tz_offset)
    
        # Now convert to naive DatetimeIndex
        return pd.DatetimeIndex(datetime_index.values + tz_offset_td64)
    • Gracias por tu respuesta! Sin embargo, creo que esto sólo funcionará si no hay verano/invierno transición en el periodo del conjunto de datos.
    • Ah, buena captura! Yo no había considerado! Voy a modificar mi solución para manejar esta situación lo antes posible.
    • Creo que esto es un error, ya que sólo calcular el desplazamiento de la primera vez y no como progreso a través del tiempo. Esto hará que se pierda el horario de verano y no se ajustará de acuerdo en que, dada la fecha y en adelante.
  5. 3

    Porque siempre tengo problemas para recordar, un resumen rápido de lo que 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')
  6. 0

    La cosa más importante es agregar tzinfo cuando se define un objeto datetime.

    from datetime import datetime, timezone
    from tzinfo_examples import HOUR, Eastern
    u0 = datetime(2016, 3, 13, 5, tzinfo=timezone.utc)
    for i in range(4):
         u = u0 + i*HOUR
         t = u.astimezone(Eastern)
         print(u.time(), 'UTC =', t.time(), t.tzname())
  7. 0

    La aceptada solución no funciona cuando hay varios diferentes zonas horarias en una Serie. Lanza ValueError: Tz-aware datetime.datetime cannot be converted to datetime64 unless utc=True

    La solución es el uso de la apply método.

    Por favor vea los siguientes ejemplos:

    # Let's have a series `a` with different multiple timezones. 
    > a
    0    2019-10-04 16:30:00+02:00
    1    2019-10-07 16:00:00-04:00
    2    2019-09-24 08:30:00-07:00
    Name: localized, dtype: object
    
    > a.iloc[0]
    Timestamp('2019-10-04 16:30:00+0200', tz='Europe/Amsterdam')
    
    # trying the accepted solution
    > a.dt.tz_localize(None)
    ValueError: Tz-aware datetime.datetime cannot be converted to datetime64 unless utc=True
    
    # Make it tz-naive. This is the solution:
    > a.apply(lambda x:x.tz_localize(None))
    0   2019-10-04 16:30:00
    1   2019-10-07 16:00:00
    2   2019-09-24 08:30:00
    Name: localized, dtype: datetime64[ns]
    
    # a.tz_convert() also does not work with multiple timezones, but this works:
    > a.apply(lambda x:x.tz_convert('America/Los_Angeles'))
    0   2019-10-04 07:30:00-07:00
    1   2019-10-07 13:00:00-07:00
    2   2019-09-24 08:30:00-07:00
    Name: localized, dtype: datetime64[ns, America/Los_Angeles]

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea