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()
で変換する必要がある。