pandasの時系列データのタイムゾーンを処理(tz_convert, tz_localize)
pandasにおいて時系列データのタイムゾーンを処理するにはtz_convert(), tz_localize()を使う。
- pandas.Series.tz_convert — pandas 1.0.3 documentation
- pandas.Series.tz_localize — pandas 1.0.3 documentation
タイムゾーン情報が設定されている(awareな)データのタイムゾーンを別のタイムゾーンに変換(変更)するのがtz_convert()で、タイムゾーン情報が設定されていない(naiveな)データに新たにタイムゾーンを設定するのがtz_localize()。
ここでは、まず単独の要素を例にtz_convert(), tz_localize()の処理内容を説明し、その後にpandas.DataFrame, Seriesのデータ列やインデックスに対する扱いを説明する。
- 文字列から
Timestamp型に変換:pd.to_datetime() - タイムゾーンを変換:
tz_convert() - タイムゾーンを新たに設定:
tz_localize() - タイムゾーンを削除する際の注意点
pandas.DataFrame,Seriesのデータ列・インデックスに対する処理- データ列での
tz_convert(),tz_localize() DatetimeIndexでのtz_convert(),tz_localize()pandas.DataFrame,Seriesでのtz_convert(),tz_localize()- タイムゾーン情報を含む文字列の場合
- データ列での
pandas.DataFrame, Seriesを時系列データとして扱う方法については以下の記事を参照。
Pythonにおけるタイムゾーンの処理については以下の記事を参照。
文字列からTimestamp型に変換: pd.to_datetime()
文字列をTimestamp型に変換するにはpd.to_datetime()関数を使う。
タイムゾーンが含まれる日付の文字列
以下のようにTのあとに時刻、さらにそのあとに+09:00(+9時間)というタイムゾーンの情報が含まれる文字列を例とする。このフォーマットはISO 8601で規定されている。
import pandas as pd
s = '2018-01-01T12:00+09:00'
print(s)
# 2018-01-01T12:00+09:00
print(type(s))
# <class 'str'>
これをpd.to_datetime()に渡すと、tz属性にタイムゾーン情報が含まれる(awareな)Timestamp型オブジェクトが返される。この例のtz属性のFixedOffset(540)は540分(= 9時間)のオフセットを表す。
ts = pd.to_datetime(s)
print(ts)
# 2018-01-01 12:00:00+09:00
print(type(ts))
# <class 'pandas._libs.tslibs.timestamps.Timestamp'>
print(ts.tz)
# pytz.FixedOffset(540)
pd.to_datetime()の引数utcをTrueとするとタイムゾーンがUTC(協定世界時)に設定される。
ts_utc = pd.to_datetime(s, utc=True)
print(ts_utc)
# 2018-01-01 03:00:00+00:00
print(ts_utc.tz)
# UTC
なお、デフォルト(utc=False)でtz属性が設定されるのはpandas0.24.0から。それより前のバージョンでは、UTCに変換され、tz属性がNoneとなる(naiveな)Timestamp型オブジェクトが返される。
タイムゾーンが含まれない日付の文字列
タイムゾーンの情報が含まれない文字列の場合、デフォルトではtz属性がNoneとなる(naiveな)Timestamp型オブジェクトが返される。
s_without_tz = '2018-01-01T12:00'
ts_naive = pd.to_datetime(s_without_tz)
print(ts_naive)
# 2018-01-01 12:00:00
print(ts_naive.tz)
# None
utc=Trueとすると、タイムゾーンがUTCに設定される。
ts_set_utc = pd.to_datetime(s_without_tz, utc=True)
print(ts_set_utc)
# 2018-01-01 12:00:00+00:00
print(ts_set_utc.tz)
# UTC
タイムゾーンを変換: tz_convert()
タイムゾーンを変換するにはtz_convert()メソッドを使う。
第一引数にタイムゾーン名を指定する。
print(ts_utc)
# 2018-01-01 03:00:00+00:00
print(ts_utc.tz)
# UTC
ts_jst = ts_utc.tz_convert('Asia/Tokyo')
print(ts_jst)
# 2018-01-01 12:00:00+09:00
print(ts_jst.tz)
# Asia/Tokyo
タイムゾーンが変換されても同時刻を指しているので、エポック秒(UNIX time)はtz_convert()前後で変わらない。Timestamp型オブジェクトのエポック秒はvalue属性で取得できる。
print(ts_utc.value)
# 1514775600000000000
print(ts_jst.value)
# 1514775600000000000
Timestampは==や<, >などの比較演算子で比較できる。同じ時刻か、どちらの時刻が早いか、などを判定できる。
print(ts_utc == ts_jst)
# True
他のタイムゾーンへの変換も同じ。
ts_pst = ts_utc.tz_convert('US/Pacific')
print(ts_pst)
# 2017-12-31 19:00:00-08:00
print(ts_pst.tz)
# US/Pacific
タイムゾーン名の一覧は以下のページなどを参照。
同じ時差を表すタイムゾーン名は一つだけでなく複数存在する。
print(ts_utc.tz_convert('America/Los_Angeles'))
# 2017-12-31 19:00:00-08:00
print(ts_utc.tz_convert('America/Vancouver'))
# 2017-12-31 19:00:00-08:00
タイムゾーンを新たに設定: tz_localize()
タイムゾーン情報が設定されていないnaiveなTimestamp型オブジェクトからtz_convert()を呼ぶとエラーになる。
print(ts_naive)
# 2018-01-01 12:00:00
print(ts_naive.tz)
# None
# print(ts_naive.tz_convert('Asia/Tokyo'))
# TypeError: Cannot convert tz-naive Timestamp, use tz_localize to localize
naiveなデータに新たにタイムゾーン情報を設定する場合はtz_localize()を使う。
ts_jst_localize = ts_naive.tz_localize('Asia/Tokyo')
print(ts_jst_localize)
# 2018-01-01 12:00:00+09:00
print(ts_jst_localize.tz)
# Asia/Tokyo
tz_localize()では、元の時刻はそのままでタイムゾーンが新たに設定されるので、同じオブジェクトに異なるタイムゾーンを設定すると違う時刻を示すオブジェクトとなる。
print(ts_naive.tz_localize('US/Pacific'))
# 2018-01-01 12:00:00-08:00
print(ts_naive.tz_localize('Asia/Tokyo') == ts_naive.tz_localize('US/Pacific'))
# False
すでにタイムゾーンが設定されているオブジェクトに対してtz_localize()で新たなタイムゾーンに上書きすることはできない。次に示すようにタイムゾーン情報を削除する必要がある。
print(ts_jst)
# 2018-01-01 12:00:00+09:00
print(ts_jst.tz)
# Asia/Tokyo
# print(ts_jst.tz_localize('US/Pacific'))
# TypeError: Cannot localize tz-aware Timestamp, use tz_convert for conversions
タイムゾーンを削除する際の注意点
タイムゾーン情報を削除したい場合は、tz_convert()またはtz_localize()の第一引数にNoneを指定すればよいが、それぞれ結果が異なるので注意。
tz_convert()の第一引数にNoneを指定すると、UTCに変換された上でタイムゾーン情報が削除され、tz_localize()の第一引数にNoneを指定すると、現地時刻のままタイムゾーン情報が削除される。
print(ts_jst)
# 2018-01-01 12:00:00+09:00
print(ts_jst.tz)
# Asia/Tokyo
print(ts_jst.tz_convert(None))
# 2018-01-01 03:00:00
print(ts_jst.tz_localize(None))
# 2018-01-01 12:00:00
pandas.DataFrame, Seriesのデータ列・インデックスに対する処理
これまでの例は単体の要素(Timestamp型)に対する処理だったが、pandas.DataFrameやpandas.Seriesに対する場合は注意が必要。
tz_convert(), tz_localize()が使えるのは、これまでの例のようなTimestamp型の要素(スカラー値)、DatetimeIndex、および、インデックスがDatetimeIndexであるpandas.DataFrame, Series。pandas.DataFrameのデータ列(pandas.Series)ではdtアクセサを介して使う。
- pandas.Timestamp.tz_convert — pandas 1.0.3 documentation
- pandas.Timestamp.tz_localize — pandas 1.0.3 documentation
- pandas.DatetimeIndex.tz_convert — pandas 1.0.3 documentation
- pandas.DatetimeIndex.tz_localize — pandas 1.0.3 documentation
- pandas.DataFrame.tz_convert — pandas 1.0.3 documentation
- pandas.DataFrame.tz_localize — pandas 1.0.3 documentation
- pandas.Series.tz_convert — pandas 1.0.3 documentation
- pandas.Series.tz_localize — pandas 1.0.3 documentation
- pandas.Series.dt.tz_convert — pandas 1.0.3 documentation
- pandas.Series.dt.tz_localize — pandas 1.0.3 documentation
データ列でのtz_convert(), tz_localize()
まず、タイムゾーン情報を含まない日時の文字列を要素とする列をもつpandas.DataFrameを例とする。
df = pd.DataFrame({'date': ['2018-01-01T12:00',
'2018-01-02T00:00',
'2018-01-03T10:00',
'2018-01-03T19:00'],
'value': ['A', 'B', 'C', 'D']})
print(df)
# date value
# 0 2018-01-01T12:00 A
# 1 2018-01-02T00:00 B
# 2 2018-01-03T10:00 C
# 3 2018-01-03T19:00 D
日時の文字列を要素とする列(pandas.Series)はpd.to_datetime()でdatetime64[ns]型に変換できる。各要素はTimestamp型。
デフォルト(utc=False)はタイムゾーン情報を含まない。
s_naive = pd.to_datetime(df['date'])
print(s_naive)
# 0 2018-01-01 12:00:00
# 1 2018-01-02 00:00:00
# 2 2018-01-03 10:00:00
# 3 2018-01-03 19:00:00
# Name: date, dtype: datetime64[ns]
print(s_naive[0])
# 2018-01-01 12:00:00
print(type(s_naive[0]))
# <class 'pandas._libs.tslibs.timestamps.Timestamp'>
print(s_naive[0].tz)
# None
utc=Trueとすると、タイムゾーンがUTCに設定される。
s_utc = pd.to_datetime(df['date'], utc=True)
print(s_utc)
# 0 2018-01-01 12:00:00+00:00
# 1 2018-01-02 00:00:00+00:00
# 2 2018-01-03 10:00:00+00:00
# 3 2018-01-03 19:00:00+00:00
# Name: date, dtype: datetime64[ns, UTC]
print(s_utc[0].tz)
# UTC
列(pandas.Series)からそのままtz_convert(), tz_localize()を呼び出すとエラーとなる。
# print(s_naive.tz_localize('Asia/Tokyo'))
# TypeError: index is not a valid DatetimeIndex or PeriodIndex
# print(s_utc.tz_convert('Asia/Tokyo'))
# TypeError: index is not a valid DatetimeIndex or PeriodIndex
dt.tz_convert(), dt.tz_localize()のように、dtアクセサを用いる。
print(s_naive.dt.tz_localize('Asia/Tokyo'))
# 0 2018-01-01 12:00:00+09:00
# 1 2018-01-02 00:00:00+09:00
# 2 2018-01-03 10:00:00+09:00
# 3 2018-01-03 19:00:00+09:00
# Name: date, dtype: datetime64[ns, Asia/Tokyo]
print(s_utc.dt.tz_convert('Asia/Tokyo'))
# 0 2018-01-01 21:00:00+09:00
# 1 2018-01-02 09:00:00+09:00
# 2 2018-01-03 19:00:00+09:00
# 3 2018-01-04 04:00:00+09:00
# Name: date, dtype: datetime64[ns, Asia/Tokyo]
上述のTimestamp型の要素の場合と同様に、タイムゾーンが設定されていないnaiveなデータ列に対してtz_convert()を使ったり、タイムゾーンが設定されているawareなデータ列に対してtz_localize()を使うことはできない。以降の例でも同じ。
# print(s_naive.dt.tz_convert('Asia/Tokyo'))
# TypeError: Cannot convert tz-naive timestamps, use tz_localize to localize
# print(s_utc.dt.tz_localize('Asia/Tokyo'))
# TypeError: Already tz-aware, use tz_convert to convert.
また、dtアクセサが使えるのは各要素の型がTimestampのような日時情報を表す場合のみ。文字列の列では使えない。上の例のようにあらかじめpd.to_datetime()で変換する必要がある。
# print(df['date'].dt.tz_localize('Asia/Tokyo'))
# AttributeError: Can only use .dt accessor with datetimelike values
DatetimeIndexでのtz_convert(), tz_localize()
set_index()やindex属性に代入するなどしてdatetime64[ns]の列をインデックスに設定すると、そのインデックスはDatetimeIndexとして扱われ、日時で行を指定したりできるようになる。
df['date'] = pd.to_datetime(df['date'])
df_ts = df.set_index('date')
print(df_ts)
# value
# date
# 2018-01-01 12:00:00 A
# 2018-01-02 00:00:00 B
# 2018-01-03 10:00:00 C
# 2018-01-03 19:00:00 D
print(df_ts.index)
# DatetimeIndex(['2018-01-01 12:00:00', '2018-01-02 00:00:00',
# '2018-01-03 10:00:00', '2018-01-03 19:00:00'],
# dtype='datetime64[ns]', name='date', freq=None)
print(type(df_ts.index))
# <class 'pandas.core.indexes.datetimes.DatetimeIndex'>
print(df_ts['2018-01-03'])
# value
# date
# 2018-01-03 10:00:00 C
# 2018-01-03 19:00:00 D
DatetimeIndexはtz_convert(), tz_localize()を実行可能。タイムゾーンを変更したり追加したりできる。
print(df_ts.index.tz_localize('Asia/Tokyo'))
# DatetimeIndex(['2018-01-01 12:00:00+09:00', '2018-01-02 00:00:00+09:00',
# '2018-01-03 10:00:00+09:00', '2018-01-03 19:00:00+09:00'],
# dtype='datetime64[ns, Asia/Tokyo]', name='date', freq=None)
pandas.DataFrame, Seriesでのtz_convert(), tz_localize()
インデックスがDatetimeIndexであるpandas.DataFrame, Seriesからtz_convert(), tz_localize()を実行することも可能。
インデックスであるDatetimeIndexのタイムゾーンが変更・追加される。
print(df_ts.tz_localize('Asia/Tokyo'))
# value
# date
# 2018-01-01 12:00:00+09:00 A
# 2018-01-02 00:00:00+09:00 B
# 2018-01-03 10:00:00+09:00 C
# 2018-01-03 19:00:00+09:00 D
pandas.Seriesでも同様。
s_ts = df_ts['value']
print(s_ts)
# date
# 2018-01-01 12:00:00 A
# 2018-01-02 00:00:00 B
# 2018-01-03 10:00:00 C
# 2018-01-03 19:00:00 D
# Name: value, dtype: object
print(s_ts.tz_localize('Asia/Tokyo'))
# date
# 2018-01-01 12:00:00+09:00 A
# 2018-01-02 00:00:00+09:00 B
# 2018-01-03 10:00:00+09:00 C
# 2018-01-03 19:00:00+09:00 D
# Name: value, dtype: object
このように、pandas.DataFrame, Seriesから直接tz_convert(), tz_localize()を実行できるのはインデックスがDatetimeIndexである場合のみ。インデックスのタイムゾーンが処理される。
データ列(pandas.Series)を処理したい場合は上述のようにdtアクセサを使う。
タイムゾーン情報を含む文字列の場合
+09:00のようなタイムゾーン情報を含む文字列の場合。
タイムゾーンが全て共通
タイムゾーンが全て共通であれば、pd.to_datetime()でタイムゾーン情報を含むdatetime64[ns]に変換できる。
df = pd.DataFrame({'date': ['2018-01-01T12:00+09:00',
'2018-01-02T00:00+09:00',
'2018-01-03T10:00+09:00',
'2018-01-03T19:00+09:00'],
'value': ['A', 'B', 'C', 'D']})
print(df)
# date value
# 0 2018-01-01T12:00+09:00 A
# 1 2018-01-02T00:00+09:00 B
# 2 2018-01-03T10:00+09:00 C
# 3 2018-01-03T19:00+09:00 D
print(pd.to_datetime(df['date']))
# 0 2018-01-01 12:00:00+09:00
# 1 2018-01-02 00:00:00+09:00
# 2 2018-01-03 10:00:00+09:00
# 3 2018-01-03 19:00:00+09:00
# Name: date, dtype: datetime64[ns, pytz.FixedOffset(540)]
utc=TrueとしてUTCに変換することも可能。
print(pd.to_datetime(df['date'], utc=True))
# 0 2018-01-01 03:00:00+00:00
# 1 2018-01-01 15:00:00+00:00
# 2 2018-01-03 01:00:00+00:00
# 3 2018-01-03 10:00:00+00:00
# Name: date, dtype: datetime64[ns, UTC]
上の例と同様に、dtアクセサを利用したり、インデックスに設定してDatetimeIndexとして扱ったりできる。
print(pd.to_datetime(df['date']).dt.tz_convert('US/Pacific'))
# 0 2017-12-31 19:00:00-08:00
# 1 2018-01-01 07:00:00-08:00
# 2 2018-01-02 17:00:00-08:00
# 3 2018-01-03 02:00:00-08:00
# Name: date, dtype: datetime64[ns, US/Pacific]
df['date'] = pd.to_datetime(df['date'])
df_ts = df.set_index('date')
print(df_ts)
# value
# date
# 2018-01-01 12:00:00+09:00 A
# 2018-01-02 00:00:00+09:00 B
# 2018-01-03 10:00:00+09:00 C
# 2018-01-03 19:00:00+09:00 D
print(df_ts.index)
# DatetimeIndex(['2018-01-01 12:00:00+09:00', '2018-01-02 00:00:00+09:00',
# '2018-01-03 10:00:00+09:00', '2018-01-03 19:00:00+09:00'],
# dtype='datetime64[ns, pytz.FixedOffset(540)]', name='date', freq=None)
print(df_ts.tz_convert('US/Pacific'))
# value
# date
# 2017-12-31 19:00:00-08:00 A
# 2018-01-01 07:00:00-08:00 B
# 2018-01-02 17:00:00-08:00 C
# 2018-01-03 02:00:00-08:00 D
タイムゾーンが共通でない
タイムゾーンがバラバラの場合。
df = pd.DataFrame({'date': ['2018-01-01T12:00+09:00',
'2018-01-02T00:00+09:00',
'2018-01-03T10:00-05:00',
'2018-01-03T19:00-08:00'],
'value': ['A', 'B', 'C', 'D']})
print(df)
# date value
# 0 2018-01-01T12:00+09:00 A
# 1 2018-01-02T00:00+09:00 B
# 2 2018-01-03T10:00-05:00 C
# 3 2018-01-03T19:00-08:00 D
pd.to_datetime()のデフォルトでは各要素がPythonのdatetimeモジュールのdatetime型に変換される。datetime.datetimeのタイムゾーン情報はtzinfo属性。元の文字列の通り、各要素が別々のタイムゾーンとなる。
print(pd.to_datetime(df['date']))
# 0 2018-01-01 12:00:00+09:00
# 1 2018-01-02 00:00:00+09:00
# 2 2018-01-03 10:00:00-05:00
# 3 2018-01-03 19:00:00-08:00
# Name: date, dtype: object
print(type(pd.to_datetime(df['date'])[0]))
# <class 'datetime.datetime'>
print(pd.to_datetime(df['date'])[0].tzinfo)
# tzoffset(None, 32400)
print(pd.to_datetime(df['date'])[2].tzinfo)
# tzoffset(None, -18000)
pd.to_datetime()の引数utcをTrueとすると、各要素はTimestamp型、列(pandas.Series)としてのデータ型dtypeはUTC基準のdatetime64[ns](datetime64[ns, UTC])となる。すべての要素がUTCの時刻に変換される。
print(pd.to_datetime(df['date'], utc=True))
# 0 2018-01-01 03:00:00+00:00
# 1 2018-01-01 15:00:00+00:00
# 2 2018-01-03 15:00:00+00:00
# 3 2018-01-04 03:00:00+00:00
# Name: date, dtype: datetime64[ns, UTC]
print(type(pd.to_datetime(df['date'], utc=True)[0]))
# <class 'pandas._libs.tslibs.timestamps.Timestamp'>
後者(utc=True)はこれまでの例のとおりだが、前者(デフォルト、utc=False)はdtアクセサを使えない。
# print(pd.to_datetime(df['date']).dt.tz_convert('Asia/Tokyo'))
# ValueError: Tz-aware datetime.datetime cannot be converted to datetime64 unless utc=True
また、インデックスに設定してもDatetimeIndexではなくノーマルのIndexとして扱われるため、tz_convert(), tz_localize()は使えない。
df['date'] = pd.to_datetime(df['date'])
df_dt = df.set_index('date')
print(df_dt)
# value
# date
# 2018-01-01 12:00:00+09:00 A
# 2018-01-02 00:00:00+09:00 B
# 2018-01-03 10:00:00-05:00 C
# 2018-01-03 19:00:00-08:00 D
print(df_dt.index)
# Index([2018-01-01 12:00:00+09:00, 2018-01-02 00:00:00+09:00,
# 2018-01-03 10:00:00-05:00, 2018-01-03 19:00:00-08:00],
# dtype='object', name='date')
# print(df_dt.tz_convert('Asia/Tokyo'))
# TypeError: index is not a valid DatetimeIndex or PeriodIndex
# print(df_dt.tz_localize('Asia/Tokyo'))
# TypeError: index is not a valid DatetimeIndex or PeriodIndex
このように、タイムゾーンがバラバラの文字列に対してtz_convert(), tz_localize()を使いたい場合は、はじめにutc=Trueのpd.to_datetime()で変換する必要がある。