Vous pouvez utiliser la fonction tz_localize
pour créer un timestone Timestamp ou DateTimeIndex, mais comment faire le contraire: comment convertir un horodatage sensible en fuseau horaire en 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 pouvais supprimer le fuseau horaire en le réglant sur Aucun, mais le résultat est ensuite converti en UTC (12 heures devient 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 un fuseau horaire naïf, tout en préservant le fuseau horaire dans lequel il a été défini?
Certains contexte sur la raison pour laquelle je pose cette question: je veux travailler avec des séries temporelles naïves de fuseaux horaires (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 gérer une série de périodes prenant en compte le fuseau horaire dans mon fuseau horaire local (Europe/Bruxelles). Comme toutes mes autres données sont naïves dans le fuseau horaire (mais représentées dans mon fuseau horaire local), je souhaite convertir cette série temporelle en naïve pour pouvoir continuer à l'utiliser, mais elle doit également être représentée dans mon fuseau horaire local (supprimez donc simplement les informations de fuseau horaire, sans convertir l’heure visible par l’utilisateur en heure UTC).
Je sais que l'heure est stockée en interne au format UTC et qu'elle est convertie dans un autre fuseau horaire uniquement lorsque vous le représentez. Il doit donc y avoir une sorte de conversion lorsque je souhaite le "délocaliser". Par exemple, avec le module python datetime, vous pouvez "supprimer" le fuseau horaire comme ceci:
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>
Donc, sur cette base, je pourrais faire ce qui suit, mais je suppose que cela ne sera pas très efficace si vous travaillez avec une série temporelle plus longue:
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
Pour répondre à ma propre question, cette fonctionnalité a été ajoutée aux pandas entre-temps. À partir de de pandas 0.15.0, vous pouvez utiliser tz_localize(None)
pour supprimer le fuseau horaire, ce qui donne l'heure locale.
Voir la rubrique whatsnew: http://pandas.pydata.org/pandas-docs/stable/whatsnew.html#timezone-handling-improvements
Donc, avec mon exemple d'en haut:
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')
utiliser tz_localize(None)
supprime les informations de fuseau horaire, ce qui donne heure locale naïve:
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')
De plus, vous pouvez également utiliser tz_convert(None)
pour supprimer les informations de fuseau horaire mais convertir en UTC, donnant ainsi heure UTC naïve:
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')
C'est beaucoup plus performant que la solution datetime.replace
:
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
Je pense que vous ne pouvez pas atteindre ce que vous voulez d'une manière plus efficace que celle que vous aviez proposée.
Le problème sous-jacent est que les horodatages (comme vous semblez le savoir) se composent de deux parties. Les données représentant l'heure UTC et le fuseau horaire tz_info. Les informations de fuseau horaire sont utilisées uniquement à des fins d'affichage lors de l'impression du fuseau horaire à l'écran. Au moment de l'affichage, les données sont décalées de manière appropriée et +01: 00 (ou similaire) est ajouté à la chaîne. Supprimer la valeur de tz_info (utiliser tz_convert (tz = None)) ne modifie pas réellement les données représentant la partie naïve de l'horodatage.
Donc, la seule façon de faire ce que vous voulez est de modifier les données sous-jacentes (les pandas ne le permettent pas ... DatetimeIndex sont immuables - voir l'aide sur DatetimeIndex) ou de créer un nouvel ensemble d'objets horodatés et de les envelopper. dans un nouveau DatetimeIndex. Votre solution fait ce dernier:
pd.DatetimeIndex([i.replace(tzinfo=None) for i in t])
Pour référence, voici la méthode replace
de Timestamp
(voir tslib.pyx):
def replace(self, **kwds):
return Timestamp(datetime.replace(self, **kwds),
offset=self.offset)
Vous pouvez vous référer aux docs sur datetime.datetime
pour constater que datetime.datetime.replace
crée également un nouvel objet.
Si vous le pouvez, votre meilleur pari pour l'efficacité est de modifier la source des données afin qu'elle rapporte (de manière incorrecte) les horodatages sans leur fuseau horaire. Vous avez mentionné:
Je veux travailler avec des séries temporelles naïves de fuseaux horaires (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)
Je serais curieux de savoir à quel tracas supplémentaire vous faites allusion. Je recommande en règle générale, pour tout développement de logiciel, de conserver les «valeurs naïves» de votre horodatage en UTC. Il n’ya guère de pire que de regarder deux valeurs int64 différentes en se demandant à quel fuseau horaire elles appartiennent. Si vous utilisez toujours, toujours, toujours le temps UTC pour le stockage interne, vous éviterez ainsi d'innombrables maux de tête. Mon mantra est Les fuseaux horaires sont réservés aux E/S humaines .
La définition de l'attribut tz
de l'index semble fonctionner explicitement:
ts_utc = ts.tz_convert("UTC")
ts_utc.index.tz = None
Le plus important 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())
S'appuyant sur la suggestion de D.A. selon laquelle " la seule façon de faire ce que vous voulez est de modifier les données sous-jacentes " et d'utiliser numpy pour modifier les données sous-jacentes ...
Cela fonctionne pour moi et 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)