Pandas DataFrameで日時データのタイムゾーン変換

TimeStampデータのタイムゾーン変換問題

PandasでTimeStampデータのタイムゾーンを変換する場合には少し気をつけないといけない。 例を使って説明していく。

以下のような test.csv というデータファイルがあるとする。 時刻データがUTCであるとし、JSTに変換したいと仮定する。

2016-03-19 03:49:12,168
2016-03-19 03:50:11,110
2016-03-30 02:10:30,9
2016-03-30 02:12:47,36
2016-03-30 02:15:03,113
2016-03-30 02:29:03,17
2016-03-30 02:31:03,8
2016-03-30 02:37:36,14
2016-03-30 02:40:37,11
2016-03-30 23:13:23,199

まずはPandasでこれを読み込む。

>>> import pandas as pd
>>> data = pd.read_csv("test.csv", header=None, names=["on_create", "value"])
>>> data
             on_create  value
0  2016-03-19 03:49:12    168
1  2016-03-19 03:50:11    110
2  2016-03-30 02:10:30      9
3  2016-03-30 02:12:47     36
4  2016-03-30 02:15:03    113
5  2016-03-30 02:29:03     17
6  2016-03-30 02:31:03      8
7  2016-03-30 02:37:36     14
8  2016-03-30 02:40:37     11
9  2016-03-30 23:13:23    199

現時点では on_create 列は文字列になっているので、TimeStamp型に変換する。

>>> type(data.on_create[0])
<type 'str'>
>>> data.on_create = pd.to_datetime(data.on_create, utc=True)
>>> type(data.on_create[0])
<class 'pandas.tslib.Timestamp'>

Padasには tz_convert なるものがあるのでこれを適用してみる。 が、エラーを食らってしまう。

>>> data.tz_convert('Asia/Tokyo')
TypeError: index is not a valid DatetimeIndex or PeriodIndex

DataFrameの列(Series)に対して適用しても同じ。

>>> data.on_create.tz_convert('Asia/Tokyo')
TypeError: index is not a valid DatetimeIndex or PeriodIndex

どうすればいいか?

tz_convert はindexに対して適用する手法らしい。 なので、以下のようにindexに変換してから適用する。

>>> data.index = pd.DatetimeIndex(data.on_create, name='on_create')

タイムゾーンが指定されていないのでUTCとして指定する。

>>> data.index = data.index.tz_localize('UTC')

タイムゾーンをJSTに変換する。

>>> data.index = data.index.tz_convert('Asia/Tokyo')
>>> data
                                    on_create  value
on_create
2016-03-19 12:49:12+09:00 2016-03-19 03:49:12    168
2016-03-19 12:50:11+09:00 2016-03-19 03:50:11    110
2016-03-30 11:10:30+09:00 2016-03-30 02:10:30      9
2016-03-30 11:12:47+09:00 2016-03-30 02:12:47     36
2016-03-30 11:15:03+09:00 2016-03-30 02:15:03    113
2016-03-30 11:29:03+09:00 2016-03-30 02:29:03     17
2016-03-30 11:31:03+09:00 2016-03-30 02:31:03      8
2016-03-30 11:37:36+09:00 2016-03-30 02:37:36     14
2016-03-30 11:40:37+09:00 2016-03-30 02:40:37     11
2016-03-31 08:13:23+09:00 2016-03-30 23:13:23    199

もちろん以下のようにして一気に適用してもOK。

>>> data.index = pd.DatetimeIndex(data.on_create, name='on_create').tz_localize('UTC').tz_convert('Asia/Tokyo')

indexのままでも使えるが、必要があれば元のデータをタイムゾーン変換済みデータで置き換え、indexを振り直す。

>>> data.on_create = data.index
>>> data = data.reset_index(drop=True)
>>> data
                  on_create  value
0 2016-03-19 12:49:12+09:00    168
1 2016-03-19 12:50:11+09:00    110
2 2016-03-30 11:10:30+09:00      9
3 2016-03-30 11:12:47+09:00     36
4 2016-03-30 11:15:03+09:00    113
5 2016-03-30 11:29:03+09:00     17
6 2016-03-30 11:31:03+09:00      8
7 2016-03-30 11:37:36+09:00     14
8 2016-03-30 11:40:37+09:00     11
9 2016-03-31 08:13:23+09:00    199

まとめ

PandasのDataFrameでTimeStamp型を使うならindexにしとけ!