パンダのタイムゾーン対応のDateTimeIndexをナイーブタイムスタンプに変換しますが、特定のタイムゾーンで


99

この関数tz_localizeを使用して、タイムスタンプまたはDateTimeIndexのタイムゾーンを認識させることができますが、その逆を行うにはどうすればよいでしょうか。タイムゾーンを保持しながら、タイムスタンプを認識しているタイムスタンプを単純なタイムスタンプに変換するにはどうすればよいでしょうか。

例:

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

タイムゾーンを[なし]に設定することで削除できますが、結果は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をタイムゾーンナイーブに変換する別の方法はありますが、それが設定されたタイムゾーンを保持しますか?


私がこれを求めている理由に関するいくつかのコンテキスト:タイムゾーンのナイーブな時系列で作業したい(タイムゾーンでの余分な煩わしさを避けるため、そして私が取り組んでいる場合にはそれらを必要としない)。
しかし、何らかの理由で、ローカルタイムゾーン(ヨーロッパ/ブリュッセル)でタイムゾーンを意識した時系列を処理する必要があります。他のすべてのデータはタイムゾーンナイーブであるため(ただし、ローカルタイムゾーンで表されます)、このタイムシリーズをナイーブに変換してさらに作業しますが、ローカルタイムゾーンでも表す必要があります(タイムゾーン情報を削除するだけです。ユーザーに表示される時刻を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

Timezone = NoneはUTCを意味します...あなたがここで何を求めているのか理解できません。
アンディ・ヘイデン2013年

説明を追加しました。私はあなたがユーザーとして「見る」時間を保ちたいと思います。これで少しわかりやすくなるといいのですが。
joris 2013年

ああ、そうです、私はあなたがそれをでできることに気づいていませんでしたreplace
アンディ・ヘイデン2013年

@AndyHaydenですから、実際に私が欲しいのは、日時に対して行うtz_localizeことの正反対ですが、replace(tzinfo=None)それは実際にはあまり明白な方法ではありません。
joris 2013年

回答:


123

私自身の質問に答えるために、この機能はその間にパンダに追加されました。開始パンダ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

1
場合は、すでにUTCとローカル時刻に変換し、する必要がある何かであなたがしている作業、その後:タイムゾーンをドロップfrom tzlocal import get_localzonetz_here = get_localzone()<datetime object>.tz_convert(tz_here).tz_localize(None)
ネイサン・ロイド

3
有用なインデックスがない場合は、t.dt.tz_localize(None)またはが必要になる場合がありますt.dt.tz_convert(None)。に注意してください.dt
acumenus 2018年

2
このソリューションは、シリーズに1つの固有のtzがある場合にのみ機能します。あなたは、同じシリーズ内で複数の異なるTZを持っている場合は、参照(とupvote)ここソリューション:-):stackoverflow.com/a/59204751/1054154
tozCSS

14

あなたが提案したよりも効率的な方法であなたが望むことを達成することはできないと思います。

根本的な問題は、タイムスタンプが(ご存知のように)2つの部分で構成されていることです。UTC時刻、およびタイムゾーンtz_infoを表すデータ。タイムゾーン情報は、タイムゾーンを画面に印刷する際の表示目的でのみ使用されます。表示時に、データは適切にオフセットされ、+ 01:00(または同様のもの)が文字列に追加されます。(tz_convert(tz = None)を使用して)tz_info値を取り除いても、タイムスタンプの単純な部分を表すデータは実際には変更されません。

したがって、必要なことを行う唯一の方法は、基になるデータを変更することです(パンダはこれを許可しません... DatetimeIndexは不変です-DatetimeIndexのヘルプを参照してください)、またはタイムスタンプオブジェクトの新しいセットを作成してそれらをラップします新しい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で保持することをお勧めします。2つの異なるint64値を見て、それらがどのタイムゾーンに属しているのか疑問に思うよりも悪いことはほとんどありません。内部ストレージに常にUTCを常に使用する場合は、数え切れないほどの頭痛の種を避けることができます。私のモットーは、タイムゾーンは人間のI / O専用です。


3
回答をありがとう、そして返信が遅れました。私のケースはアプリケーションではなく、自分の仕事の科学的分析にすぎません(たとえば、世界中の共同研究者と共有することはありません)。その場合、単純なタイムスタンプを使用する方が簡単ですが、現地時間で作業する方が簡単です。したがって、タイムゾーンについて心配する必要はなく、タイムスタンプを現地時間として解釈できます(余分な「面倒」は、たとえば、すべてがタイムゾーン内にある必要がある場合などです。そうしないと、「オフセットを比較できません-ナイーブでオフセットを意識した日時」)。しかし、より複雑なアプリケーションを扱う場合は、完全に同意します。
joris 2013

12

私はいつも覚えるのに苦労しているので、これらのそれぞれが何をするかについての簡単な要約:

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

7

tzインデックスの属性を明示的に設定すると、うまくいくようです。

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

3
遅いコメントですが、結果をUTCではなくローカルタイムゾーンで表された時間にしたいです。また、質問で示したように、tzを[なし]に設定すると、UTCにも変換されます。
joris 2016年

さらに、時系列はすでにタイムゾーンを認識しているため、それを呼び出すとtz_convertエラーが発生します。
joris 2016年

4

シリーズに複数の異なるタイムゾーンがある場合、受け入れられたソリューションは機能しません。投げる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]

3

必要なことを行う唯一の方法は、基になるデータを変更することですという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)

ご回答有難うございます!ただし、これは、データセットの期間に夏時間/冬時間の遷移がない場合にのみ機能すると思います。
joris 2013

@jorisああ、いいキャッチ!私はそれを考えていませんでした!この状況をできるだけ早く処理するようにソリューションを変更します。
ジャックケリー

これはまだ間違っていると思います。最初のオフセットを計算しているだけであり、時間の経過とともに進行するわけではないからです。これにより、夏時間が失われ、その特定の日付以降に応じて調整されなくなります。
Pierre-

2

貢献が遅れましたが、Pythonの日時で似たようなものに出くわし、パンダは同じ日付に異なるタイムスタンプを付けます

にタイムゾーン対応の日時があるpandas場合、技術的にtz_localize(None)は、タイムスタンプからの現地時間がUTCであるかのように、POSIXタイムスタンプ(内部で使用される)を変更します。 ローカルこの文脈では意味指定された時間帯でローカル。例:

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タイムスタンプを変更しない、または使用できる場合にのみ、タイムゾーン対応の日時を使用します。その場合、実際にはUTCを使用していることを覚えておいてください。

(Windows 10、pandasv1.0.5上のPython 3.8.2 x64 )


0

最も重要なことはtzinfo、日時オブジェクトを定義するときに追加することです。

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())
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.