Development Tip

Pandas 시간대 인식 DateTimeIndex를 순진한 타임 스탬프로 변환하지만 특정 시간대

yourdevel 2020. 11. 11. 20:45
반응형

Pandas 시간대 인식 DateTimeIndex를 순진한 타임 스탬프로 변환하지만 특정 시간대


이 함수 tz_localize를 사용하여 Timestamp 또는 DateTimeIndex 시간대를 인식하도록 만들 수 있지만 그 반대의 경우 어떻게 할 수 있습니까? 시간대를 유지하면서 시간대 인식 Timestamp를 순진한 것으로 변환하려면 어떻게해야합니까?

예 :

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

None으로 설정하여 시간대를 제거 할 수 있지만 결과는 UTC로 변환됩니다 (12 시가 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

DateTimeIndex를 시간대 순진한 시간대로 변환 할 수있는 또 다른 방법이 있지만 설정된 시간대를 유지하면서?


내가 이것을 묻는 이유에 대한 몇 가지 컨텍스트 : 시간대 순진한 타임 시리즈로 작업하고 싶습니다 (시간대에 대한 추가 번거 로움을 피하기 위해 작업중 인 경우에는 필요하지 않습니다).
하지만 어떤 이유에서인지 현지 시간대 (Europe / Brussels)에서 시간대 인식 시계열을 처리해야합니다. 내 다른 모든 데이터는 표준 시간대 순진하지만 (내 현지 시간대로 표시됨) 추가 작업을 위해이 timeseries를 순진한 것으로 변환하고 싶지만 내 현지 시간대로 표시되어야합니다 (시간대 정보를 제거하고, 사용자가 볼 수있는 시간을 UTC 로 변환하지 않고 ).

나는 시간이 실제로 내부적으로 UTC로 저장되고 당신이 그것을 표현할 때 다른 시간 대로만 변환된다는 것을 알고있다. 그래서 내가 그것을 "해제"하고 싶을 때 어떤 종류의 변환이 있어야한다. 예를 들어, python datetime 모듈을 사용하면 다음과 같이 시간대를 "제거"할 수 있습니다.

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> 

따라서이를 기반으로 다음을 수행 할 수 있지만 더 큰 시계열로 작업 할 때는 매우 효율적이지 않을 것이라고 생각합니다.

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

내 질문에 답하기 위해 그 동안이 기능이 pandas에 추가되었습니다. pandas 0.15.0 부터 사용 tz_localize(None)하여 시간대를 제거하여 현지 시간을 만들 수 있습니다 .
whatsnew 항목을 참조하십시오 : http://pandas.pydata.org/pandas-docs/stable/whatsnew.html#timezone-handling-improvements

따라서 위의 예를 들면 다음과 같습니다.

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')

를 사용 tz_localize(None)하면 시간대 정보가 제거되어 순진한 현지 시간이됩니다 .

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')

또한을 사용 tz_convert(None)하여 시간대 정보를 제거하지만 UTC로 변환하여 순진한 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')

이것은 솔루션 보다 훨씬 더 성능이 좋습니다 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

당신이 제안한 것보다 더 효율적으로 원하는 것을 달성 할 수 없다고 생각합니다.

The underlying problem is that the timestamps (as you seem aware) are made up of two parts. The data that represents the UTC time, and the timezone, tz_info. The timezone information is used only for display purposes when printing the timezone to the screen. At display time, the data is offset appropriately and +01:00 (or similar) is added to the string. Stripping off the tz_info value (using tz_convert(tz=None)) doesn't doesn't actually change the data that represents the naive part of the timestamp.

So, the only way to do what you want is to modify the underlying data (pandas doesn't allow this... DatetimeIndex are immutable -- see the help on DatetimeIndex), or to create a new set of timestamp objects and wrap them in a new DatetimeIndex. Your solution does the latter:

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

For reference, here is the replace method of Timestamp (see tslib.pyx):

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

You can refer to the docs on datetime.datetime to see that datetime.datetime.replace also creates a new object.

If you can, your best bet for efficiency is to modify the source of the data so that it (incorrectly) reports the timestamps without their timezone. You mentioned:

I want to work with timezone naive timeseries (to avoid the extra hassle with timezones, and I do not need them for the case I am working on)

I'd be curious what extra hassle you are referring to. I recommend as a general rule for all software development, keep your timestamp 'naive values' in UTC. There is little worse than looking at two different int64 values wondering which timezone they belong to. If you always, always, always use UTC for the internal storage, then you will avoid countless headaches. My mantra is Timezones are for human I/O only.


Setting the tz attribute of the index explicitly seems to work:

ts_utc = ts.tz_convert("UTC")
ts_utc.index.tz = None

Building on D.A.'s suggestion that "the only way to do what you want is to modify the underlying data" and using numpy to modify the underlying data...

This works for me, and is pretty fast:

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)

The most important thing is add tzinfo when you define a datetime object.

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())

Because I always struggle to remember, a quick summary of what each of these do:

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

>>> pd.Timestamp.utcnow()  # tz aware UTC
Timestamp('2019-10-07 08:30:19.428748+0200', 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+0200')

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

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

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

참고URL : https://stackoverflow.com/questions/16628819/convert-pandas-timezone-aware-datetimeindex-to-naive-timestamp-but-in-certain-t

반응형