167 votes

Convertir l'index DateTimeIndex de pandas, qui tient compte du fuseau horaire, en un horodatage naïf, mais dans un certain fuseau horaire.

Vous pouvez utiliser la fonction tz_localize pour rendre un Timestamp ou un DateTimeIndex conscient du fuseau horaire, mais comment faire le contraire : comment convertir un Timestamp conscient du fuseau horaire en un Timestamp naïf, tout en préservant son fuseau horaire ?

Un exemple :

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

Je pourrais supprimer le fuseau horaire en lui attribuant la valeur None, mais le résultat est alors converti en UTC (12 heures deviennent 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

Existe-t-il un autre moyen de convertir un DateTimeIndex en fuseau horaire naïf, mais en conservant le fuseau horaire dans lequel il a été défini ?


Quelques contexte sur la raison pour laquelle je demande ça : Je veux travailler avec des séries temporelles sans fuseau horaire (pour éviter les tracas supplémentaires liés aux fuseaux horaires, et je n'en ai pas besoin pour le cas sur lequel je travaille).
Mais pour une raison quelconque, je dois traiter une série temporelle dans mon fuseau horaire local (Europe/Bruxelles). Comme toutes mes autres données ne tiennent pas compte du fuseau horaire (mais sont représentées dans mon fuseau horaire local), je veux convertir cette série temporelle en série naïve pour pouvoir continuer à travailler avec elle, mais elle doit également être représentée dans mon fuseau horaire local (il suffit donc de supprimer les informations relatives au fuseau horaire, sans convertir la série temporelle en série naïve). visible par l'utilisateur l'heure en UTC).

Je sais que l'heure est en fait stockée en interne en UTC et qu'elle n'est convertie en un autre fuseau horaire que lorsque vous la représentez, il doit donc y avoir une sorte de conversion lorsque je veux la "délocaliser". Par exemple, avec le module python datetime, vous pouvez "supprimer" le fuseau horaire de la manière suivante :

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> 

Sur cette base, je pourrais faire ce qui suit, mais je suppose que cela ne sera pas très efficace si je travaille avec une série chronologique plus importante :

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

6voto

MrFuppes Points 11659

Contribution tardive mais je viens de tomber sur quelque chose de similaire dans Python datetime et pandas donnent des horodatages différents pour la même date .

Si vous avez un temps de date sensible au fuseau horaire dans pandas , techniquement, tz_localize(None) modifie le timestamp POSIX (qui est utilisé en interne) comme si l'heure locale du timestamp était UTC. Local dans ce contexte, signifie local dans le fuseau horaire spécifié . Ex :

import pandas as pd

t = pd.date_range(start="2013-05-18 12:00:00", periods=2, freq='H', tz="US/Central")
# DatetimeIndex(['2013-05-18 12:00:00-05:00', '2013-05-18 13:00:00-05:00'], dtype='datetime64[ns, US/Central]', freq='H')

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

# offset in seconds according to timezone:
(t_loc.values-t.values)//1e9
# array([-18000, -18000], dtype='timedelta64[ns]')

Notez que cela vous laissera avec des choses étranges pendant les transitions DST par exemple

t = pd.date_range(start="2020-03-08 01:00:00", periods=2, freq='H', tz="US/Central")
(t.values[1]-t.values[0])//1e9
# numpy.timedelta64(3600,'ns')

t_loc = t.tz_localize(None)
(t_loc.values[1]-t_loc.values[0])//1e9
# numpy.timedelta64(7200,'ns')

En revanche, tz_convert(None) ne modifie pas l'horodatage interne, il supprime simplement l'élément tzinfo .

t_utc = t.tz_convert(None)
(t_utc.values-t.values)//1e9
# array([0, 0], dtype='timedelta64[ns]')

En conclusion, je dirais qu'il faut s'en tenir à une date sensible au fuseau horaire si vous le pouvez, ou n'utiliser que la fonction t.tz_convert(None) qui ne modifie pas le timestamp POSIX sous-jacent. Gardez à l'esprit que vous travaillez alors pratiquement avec UTC.

(Python 3.8.2 x64 sur Windows 10, pandas v1.0.5.)

4voto

Jack Kelly Points 553

En s'appuyant sur la suggestion de D.A. que " le seul moyen de faire ce que vous voulez est de modifier les données sous-jacentes. " et en utilisant numpy pour modifier les données sous-jacentes...

Cela fonctionne pour moi, et c'est assez rapide :

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)

0voto

Yuchao Jiang Points 652

La chose la plus importante est d'ajouter tzinfo lorsque vous définissez un objet 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())

0voto

charel-f Points 414

Comment j'ai traité ce problème avec un datetimeindex de fréquence 15-min en Europe.

Si vous êtes dans la situation où vous avez une connaissance du fuseau horaire ( Europe/Amsterdam dans mon cas) et je veux le convertir en un indice de fuseau horaire naïf index en transformant tout en heure locale, vous aurez des problèmes dst, à savoir

  • il manquera une heure le dernier dimanche de mars (lorsque l'Europe passera à l'heure d'été).
  • il y aura 1 heure de duplicata le dernier dimanche d'octobre (lorsque l'Europe passe à l'heure d'été)

Voici comment vous pouvez y faire face :

# make index tz naive
df.index = df.index.tz_localize(None)

# handle dst
if df.index[0].month == 3:
    # last sunday of march, one hour is lost
    df = df.resample("15min").pad()

if df.index[0].month == 10:
    # in october, one hour is added
    df = df[~df.index.duplicated(keep='last')]

Note : dans mon cas, j'exécute le code ci-dessus sur une df qui ne contient qu'un seul mois, donc je fais df.index[0].month pour connaître le mois. Si le vôtre contient plus de mois, vous devriez probablement l'indexer différemment pour savoir quand faire le DST.

Il consiste à rééchantillonner à partir de la dernière valeur valide en mars, pour éviter de perdre l'heure (dans mon cas, toutes mes données sont dans des intervalles de 15 min, donc je rééchantillonne comme ça. Rééchantillonnez pour n'importe quel intervalle). Et pour octobre, je supprime les doublons.

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