Możesz użyć tej funkcji, tz_localize
aby wskazać strefę czasową Timestamp lub DateTimeIndex, ale jak możesz zrobić coś przeciwnego: jak przekonwertować sygnaturę czasową uwzględniającą strefę czasową na naiwną, zachowując jej strefę czasową?
Przykład:
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
Mogłem usunąć strefę czasową, ustawiając ją na Brak, ale wynik jest konwertowany na UTC (godzina 12 stała się 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
Czy jest inny sposób na przekonwertowanie DateTimeIndex na naiwną strefę czasową, ale zachowując strefę czasową, w której została ustawiona?
Pewien kontekst na temat powodu, dla którego pytam: chcę pracować z naiwnymi seriami czasowymi dotyczącymi stref czasowych (aby uniknąć dodatkowych problemów ze strefami czasowymi i nie potrzebuję ich w przypadku, nad którym pracuję).
Ale z jakiegoś powodu mam do czynienia z seriami czasowymi uwzględniającymi strefę czasową w mojej lokalnej strefie czasowej (Europa / Bruksela). Ponieważ wszystkie moje inne dane są naiwne dla strefy czasowej (ale reprezentowane w mojej lokalnej strefie czasowej), chcę przekonwertować tę serię czasową na naiwną, aby dalej z nią pracować, ale musi również być reprezentowana w mojej lokalnej strefie czasowej (więc po prostu usuń informacje o strefie czasowej, bez konwersji czasu widocznego dla użytkownika na UTC).
Wiem, że czas jest w rzeczywistości przechowywany wewnętrznie jako UTC i jest konwertowany na inną strefę czasową tylko wtedy, gdy go reprezentujesz, więc musi nastąpić jakaś konwersja, gdy chcę go „zdelokalizować”. Na przykład za pomocą modułu datetime w języku python można „usunąć” strefę czasową w następujący sposób:
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>
Na tej podstawie mógłbym więc wykonać następujące czynności, ale przypuszczam, że nie będzie to zbyt wydajne podczas pracy z większymi seriami czasu:
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
replace
.tz_localize
coreplace(tzinfo=None)
robi w przypadku dat, ale w rzeczywistości nie jest to bardzo oczywisty sposób.Odpowiedzi:
Odpowiadając na moje własne pytanie, w międzyczasie ta funkcja została dodana do pand. Począwszy od pandy 0.15.0 , możesz użyć,
tz_localize(None)
aby usunąć strefę czasową skutkującą czasem lokalnym.Zobacz wpis whatsnew: http://pandas.pydata.org/pandas-docs/stable/whatsnew.html#timezone-handling-improvements
A więc z moim przykładem z góry:
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')
użycie
tz_localize(None)
usuwa informacje o strefie czasowej, powodując naiwny czas lokalny :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')
Ponadto możesz również użyć
tz_convert(None)
do usunięcia informacji o strefie czasowej, ale konwertując na UTC, uzyskując naiwny czas 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')
Jest to znacznie wydajniejsze niż
datetime.replace
rozwiązanie: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
źródło
from tzlocal import get_localzone
,tz_here = get_localzone()
,<datetime object>.tz_convert(tz_here).tz_localize(None)
t.dt.tz_localize(None)
lubt.dt.tz_convert(None)
. Zwróć uwagę na.dt
.Myślę, że nie możesz osiągnąć tego, co chcesz, w bardziej efektywny sposób niż proponowałeś.
Podstawowy problem polega na tym, że znaczniki czasu (jak wydajesz się świadomy) składają się z dwóch części. Dane reprezentujące czas UTC i strefę czasową tz_info. Informacje o strefie czasowej są używane tylko do celów wyświetlania podczas drukowania strefy czasowej na ekranie. W czasie wyświetlania dane są odpowiednio przesunięte i do ciągu dodawane jest +01: 00 (lub podobne). Usunięcie wartości tz_info (przy użyciu tz_convert (tz = None)) nie zmienia w rzeczywistości danych, które reprezentują naiwną część znacznika czasu.
Tak więc jedynym sposobem na zrobienie tego, co chcesz, jest zmodyfikowanie podstawowych danych (pandy nie pozwalają na to ... DatetimeIndex są niezmienne - zobacz pomoc dotyczącą DatetimeIndex) lub utworzenie nowego zestawu obiektów ze znacznikami czasu i zawijanie ich w nowym DatetimeIndex. Twoje rozwiązanie robi to drugie:
pd.DatetimeIndex([i.replace(tzinfo=None) for i in t])
Dla odniesienia, oto
replace
metodaTimestamp
(patrz tslib.pyx):def replace(self, **kwds): return Timestamp(datetime.replace(self, **kwds), offset=self.offset)
Możesz odnieść się do dokumentów,
datetime.datetime
aby zobaczyć, żedatetime.datetime.replace
również tworzy nowy obiekt.Jeśli możesz, najlepszym sposobem na zwiększenie wydajności jest zmodyfikowanie źródła danych tak, aby (nieprawidłowo) zgłaszało sygnatury czasowe bez ich strefy czasowej. Wspomniałeś:
Byłbym ciekawy, o jakim dodatkowym kłopocie masz na myśli. Generalnie zalecam, aby w przypadku tworzenia oprogramowania zachować „naiwne wartości” sygnatury czasowej w UTC. Nie ma nic gorszego niż przyjrzenie się dwóm różnym wartościom int64 zastanawianie się, do której strefy czasowej należą. Jeśli zawsze, zawsze, zawsze używasz UTC jako pamięci wewnętrznej, unikniesz niezliczonych bólów głowy. Moja mantra brzmi: Strefy czasowe są przeznaczone wyłącznie dla ludzkich wejść / wyjść .
źródło
Ponieważ zawsze staram się zapamiętać, krótkie podsumowanie tego, co robią:
>>> 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')
źródło
tz
Wydaje się, że ustawienie atrybutu indeksu wyraźnie działa:ts_utc = ts.tz_convert("UTC") ts_utc.index.tz = None
źródło
tz
Brak również konwertuje je na UTC.tz_convert
jej spowoduje zgłoszenie błędu.Opierając się na sugestii DA, że „ jedynym sposobem na zrobienie tego, co chcesz, jest modyfikacja danych bazowych ” i użycie numpy do zmodyfikowania danych bazowych ...
To działa dla mnie i jest dość szybkie:
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)
źródło
Przyjęte rozwiązanie nie działa, gdy w serii jest wiele różnych stref czasowych. Rzuca
ValueError: Tz-aware datetime.datetime cannot be converted to datetime64 unless utc=True
Rozwiązaniem jest użycie
apply
metody.Zobacz poniższe przykłady:
# 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]
źródło
Późny wkład, ale właśnie natknąłem się na coś podobnego w Pythonie data i godzina, a pandy podają różne sygnatury czasowe dla tej samej daty .
Jeśli masz datę i godzinę uwzględniającą strefę czasową w
pandas
, technicznie rzecz biorąc,tz_localize(None)
zmienia znacznik czasu POSIX (który jest używany wewnętrznie), tak jakby lokalny czas z sygnatury czasowej był UTC. Lokalny w tym kontekście oznacza lokalny w określonej strefie czasowej . Dawny: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]')
Zauważ, że spowoduje to dziwne rzeczy podczas przejścia na czas letni , np
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')
W przeciwieństwie do
tz_convert(None)
tego nie modyfikuje wewnętrznego znacznika czasu, po prostu usuwa rozszerzenietzinfo
.t_utc = t.tz_convert(None) (t_utc.values-t.values)//1e9 # array([0, 0], dtype='timedelta64[ns]')
Mój wniosek byłby następujący: trzymaj się daty i godziny uwzględniającej strefę czasową, jeśli możesz lub używaj tylko tego,
t.tz_convert(None)
co nie modyfikuje bazowego znacznika czasu POSIX. Pamiętaj tylko, że wtedy praktycznie pracujesz z UTC.(Python 3.8.2 x64 w systemie Windows 10,
pandas
wersja 1.0.5.)źródło
Najważniejszą rzeczą jest dodanie
tzinfo
podczas definiowania obiektu daty i godziny.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())
źródło