【pandas】時系列データの時間の頻度を変更する【Python】
時系列データを扱っている場合、
- 1分足データを5分足にしたい
- 5分足データを1時間足データにしたい
- 1時間足データを1日足データにしたい
などをしたいときがある。
データ準備
本記事では1分足データをベースに頻度を変更していく。
1分足データの作成方法は以下の記事を参照。
データを様々な頻度に変換する
DataFrame.resample(freq)でfreqで指定した頻度に変換できる。
resampleの前提として、DataFrameのindexがDatetimeIndexになっている必要がある。
df.set_index()でインデックスを設定する場合、pd.to_datetime()で日付型に変換してからindexに指定する。
1# date_colを日付型に変換する。必要に応じてformatを指定する。
2df_1min[date_col] = pd.to_datetime(df_1min[date_col])
3# 日付型の列をインデックスに指定する。
4df_1min.set_index(date_col, inplace=True)
meanやsumなどで平均値や合計値などを計算できる。
1# 5分足に変換。平均値を計算。
2df_5min = df_1min.resample("5T").mean()
3# 30分足に変換。合計値を計算。
4df_30min = df_1min.resample("30T").sum()
5# 1時間足に変換。最大値を取得。
6df_1h = df_1min.resample("H").max()
7# 2時間足に変換。最小値を取得。
8df_2h = df_5min.resample("2H").min()
9# 1日足に変換。中央値を取得。
10df_1d = df_5min.resample("D").median()
11
なお、freqだけを指定した場合、00分、00時などを基準にデータが集約される。
1print(df_5min)
2# time six_hour_wave one_day_wave one_week_wave \
3# datetime
4# 2020-04-15 00:00:00 0.01 0.02 0.03 -0.01
5# 2020-04-15 00:05:00 0.03 0.03 0.04 -0.05
6# 2020-04-15 00:10:00 0.05 -0.04 0.04 -0.01
7# 2020-04-15 00:15:00 0.07 -0.03 0.01 0.02
8# 2020-04-15 00:20:00 0.10 0.01 0.05 0.01
df.resample(“5T”).mean()で集約したデータは0:00から始まり、0:05, 0:10, …と続く。
中途半端な時間から始まるデータの集約
resampleは実用上は大抵の場合、freqだけを意識すれば問題ないが、どのように集約されているのかを知ると理解が深まり応用範囲が広がる。
とは言っても、割と直感と一致する動作なので、そこまで理解しなくても問題ないかもしれない・・・。
00分以外から始まるデータの作成
例として、9:02から始まる1分足のデータを作成する。
1n_points = 1000
2increment_data = np.arange(1, n_points + 1)
3
4date_index = pd.date_range(start="2020/4/1 9:02", periods=n_points, freq="T")
5df_T_from02 = pd.DataFrame(increment_data, index=date_index, columns=["value"])
6print(df_T_from02)
7# value
8# 2020-04-01 09:02:00 1
9# 2020-04-01 09:03:00 2
10# 2020-04-01 09:04:00 3
11# 2020-04-01 09:05:00 4
12# 2020-04-01 09:06:00 5
13# ... ...
14# 2020-04-02 01:37:00 996
15# 2020-04-02 01:38:00 997
16# 2020-04-02 01:39:00 998
17# 2020-04-02 01:40:00 999
18# 2020-04-02 01:41:00 1000
19
20# [1000 rows x 1 columns]
5分足に集約する
freqだけを指定してresample(“5T”).mean()で5分間の平均値を計算する場合、以下のような結果になる。
1df_5T_from02 = df_T_from02.resample("5T").mean()
2print(df_5T_from02)
3# value
4# 2020-04-01 09:00:00 2.00 <- 9:00〜9:04:59までのデータの平均値。今回の場合、(1+2+3)/3=2
5# 2020-04-01 09:05:00 6.00 <- 9:05〜9:09:59までのデータの平均値。(4+5+6+7+8)/5=6
6# 2020-04-01 09:10:00 11.00
7# 2020-04-01 09:15:00 16.00
8# 2020-04-01 09:20:00 21.00
9# ... ...
10# 2020-04-02 01:20:00 981.00
11# 2020-04-02 01:25:00 986.00
12# 2020-04-02 01:30:00 991.00
13# 2020-04-02 01:35:00 996.00
14# 2020-04-02 01:40:00 999.50 <- 1:40〜1:44:59までのデータの平均値。(999+1000)/2=999.5
15
16# [201 rows x 1 columns]
5分足データ先頭の9:00の行は9:00:00〜9:04:59までのデータの平均値が計算される。
5分足データ最後の1:40の行は1:40:00〜1:44:59までのデータの平均値が計算される。
resample時の集約起点をずらす
offsetを指定することで、指定した分だけ集約する起点をずらすことができる。
offsetを指定しない場合、9:00〜始まるが、”2T”を指定すると9:02〜になる。
1df_5T_from02_offset_2 = df_T_from02.resample("5T", offset="2T").mean()
2print(df_5T_from02_offset_2)
3# value
4# 2020-04-01 09:02:00 3.00 <- 9:02〜9:06:59の平均値。(1+2+3+4+5)/5=3
5# 2020-04-01 09:07:00 8.00
6# 2020-04-01 09:12:00 13.00
7# 2020-04-01 09:17:00 18.00
8# 2020-04-01 09:22:00 23.00
9# ... ...
10# 2020-04-02 01:17:00 978.00
11# 2020-04-02 01:22:00 983.00
12# 2020-04-02 01:27:00 988.00
13# 2020-04-02 01:32:00 993.00
14# 2020-04-02 01:37:00 998.00 <- 1:37〜1:41:59の平均値。(996+997+998+999+1000)/5=998
offsetには負の値も指定可能。
1df_5T_from02_offset_n2 = df_T_from02.resample("5T", offset="-2T").mean()
2print(df_5T_from02_offset_n2)
3# value
4# 2020-04-01 08:58:00 1.00
5# 2020-04-01 09:03:00 4.00
6# 2020-04-01 09:08:00 9.00
7# 2020-04-01 09:13:00 14.00
8# 2020-04-01 09:18:00 19.00
9# ... ...
10# 2020-04-02 01:18:00 979.00
11# 2020-04-02 01:23:00 984.00
12# 2020-04-02 01:28:00 989.00
13# 2020-04-02 01:33:00 994.00
14# 2020-04-02 01:38:00 998.50
offsetを使うことで、割と柔軟に集約できる。
引数のbaseは非推奨
pandasの1系の場合、offsetの代わりにbaseも指定できるが、2系ではTypeErrorとなる。
1系でも将来廃止されるパラメータのため、非推奨となっている。
1# pandas1系では以下のワーニングが出る。2系ではエラー。
2df_nn_5T_from02_base2 = df_nn_T_from02.resample("5T", base=2).mean()
3# 1系で出力されるワーニング
4# /var/folders/t9/bnty854x48dcg46zzp6_5qhh0000gn/T/ipykernel_33314/2909543067.py:1: FutureWarning: 'base' in .resample() and in Grouper() is deprecated.
5# The new arguments that you should use are 'offset' or 'origin'.
6
7# >>> df.resample(freq="3s", base=2)
8
9# becomes:
10
11# >>> df.resample(freq="3s", offset="2s")
12
13# df_nn_5T_from02_base2 = df_nn_T_from02.resample("5T", base=2).mean()
14# 2系で出力されるエラー
15# TypeError: NDFrame.resample() got an unexpected keyword argument 'base'
(1系で非推奨(将来廃止予定)だったものは2系で廃止されてて辛い書き方に迷いがなくなる)
まとめ
1# 5分足に変換。平均値を計算。
2df_5min = df_1min.resample("5T").mean()
3# 30分足に変換。合計値を計算。
4df_30min = df_1min.resample("30T").sum()
5# 1時間足に変換。最大値を取得。
6df_1h = df_1min.resample("H").max()
7# 2時間足に変換。最小値を取得。
8df_2h = df_5min.resample("2H").min()
9# 1日足に変換。中央値を取得。
10df_1d = df_5min.resample("D").median()
11
12# 集約の起点をずらす
13df_5min_offset_2 = df_1min.resample("5T", offset="2T").mean()
14df_5min_offset_n2 = df_1min.resample("5T", offset="-2T").mean()