태그 보관물: pandas

pandas

Pandas 시간대 인식 DateTimeIndex를 순진한 타임 스탬프로 변환하지만 특정 시간대 또는 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


답변

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

근본적인 문제는 타임 스탬프 (아시다시피)가 두 부분으로 구성된다는 것입니다. UTC 시간과 시간대 tz_info를 나타내는 데이터입니다. 시간대 정보는 시간대를 화면에 인쇄 할 때 표시 목적으로 만 사용됩니다. 표시 시간에 데이터는 적절하게 오프셋되고 +01 : 00 (또는 유사)이 문자열에 추가됩니다. tz_info 값을 제거 (tz_convert (tz = None) 사용)해도 타임 스탬프의 순진한 부분을 나타내는 데이터가 실제로 변경되지는 않습니다.

따라서 원하는 작업을 수행하는 유일한 방법은 기본 데이터를 수정하거나 (pandas는이를 허용하지 않습니다 … 새로운 DatetimeIndex에서. 솔루션은 후자를 수행합니다.

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

참고로 다음 replace방법 은 다음 과 같습니다 Timestamp(tslib.pyx 참조).

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

의 문서 datetime.datetime를 참조 datetime.datetime.replace하여 새 개체도 생성 하는지 확인할 수 있습니다.

가능한 경우 효율성을위한 최선의 방법은 데이터 소스를 수정하여 타임 존없이 타임 스탬프를 (잘못)보고하도록하는 것입니다. 언급 :

시간대 순진한 시계열로 작업하고 싶습니다 (시간대에 대한 추가 번거 로움을 피하기 위해 작업중인 경우에는 필요하지 않음)

나는 당신이 어떤 추가 번거 로움을 언급하고 있는지 궁금합니다. 모든 소프트웨어 개발에 대한 일반적인 규칙으로 타임 스탬프를 UTC로 ‘순진한 값’으로 유지하는 것이 좋습니다. 어떤 시간대에 속하는지 궁금해하는 두 개의 다른 int64 값을 보는 것보다 나쁘지 않습니다. 항상 내부 저장소에 항상 UTC를 사용하면 수많은 골칫거리를 피할 수 있습니다. 내 만트라는 Timezones are for human I / O only 입니다.


답변

항상 기억하기 힘들 기 때문에 이들 각각이 수행하는 작업에 대한 간략한 요약 :

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


답변

tz색인 의 속성을 명시 적으로 설정하면 작동하는 것 같습니다.

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


답변

시리즈에 여러 시간대가있는 경우 허용되는 솔루션이 작동하지 않습니다. 던진다ValueError: Tz-aware datetime.datetime cannot be converted to datetime64 unless utc=True

해결책은 apply방법 을 사용하는 것입니다.

아래의 예를 참조하십시오.

# 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]


답변

원하는 작업을 수행하는 유일한 방법은 기본 데이터를 수정하는 것” 이라는 DA의 제안을 기반으로 하고 numpy를 사용하여 기본 데이터를 수정합니다.

이것은 나를 위해 작동하며 매우 빠릅니다.

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)


답변

늦게 기여했지만 Python datetime 에서 비슷한 것을 발견 했으며 pandas는 동일한 날짜에 대해 다른 타임 스탬프를 제공합니다 .

에서 시간대를 인식하는 datetime이있는 pandas경우 기술적으로 tz_localize(None)POSIX 타임 스탬프 (내부적으로 사용됨)를 타임 스탬프의 로컬 시간이 UTC 인 것처럼 변경합니다. 지역 이 맥락에서 의미 지정된 시간대에있는 지역 . 전의:

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

이렇게하면 DST 전환 중에 이상한 일이 발생합니다 . 예 :

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

반대로 tz_convert(None)내부 타임 스탬프를 수정하지 않고 tzinfo.

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

내 결론은 다음과 같습니다 t.tz_convert(None). 기본 POSIX 타임 스탬프를 수정할 수 없거나 사용할 수만 있으면 시간대 인식 datetime을 사용 하십시오. 그때 실제로 UTC로 작업하고 있음을 명심하십시오.

(Windows 10, pandasv1.0.5의 Python 3.8.2 x64 )