pandasでデータを行・列(縦・横)方向にずらすshift
pandas.DataFrame
, pandas.Series
のデータを行または列方向にずらすにはshift()
を使う。
行または列の差分や変化率を取得するにはdiff()
やpct_change()
が使えるのでshift()
の出番はあまりないが、時系列データの差分を取る際にshift()
だと期間を指定できるので便利。
diff()
, pct_change()
については以下の記事を参照。
ここでは以下の内容について説明する。
shift()
の使い方- ずらし幅を指定: 引数
periods
- ずらす方向を指定: 引数
axis
- ずらし幅を指定: 引数
- 時系列データに対する
shift()
- ずらす期間を指定: 引数
freq
- ずらす期間を指定: 引数
shift()の使い方
以下のpandas.DataFrame
を例とする。
import pandas as pd
df = pd.DataFrame({'a': range(1, 6),
'b': [x**2 for x in range(1, 6)],
'c': [x**3 for x in range(1, 6)]})
print(df)
# a b c
# 0 1 1 1
# 1 2 4 8
# 2 3 9 27
# 3 4 16 64
# 4 5 25 125
デフォルトでは下方向に1行ずれる。行数はそのままなので、最後の行のデータは削除される。
print(df.shift())
# a b c
# 0 NaN NaN NaN
# 1 1.0 1.0 1.0
# 2 2.0 4.0 8.0
# 3 3.0 9.0 27.0
# 4 4.0 16.0 64.0
欠損値NaN
の処理については以下の記事を参照。
なお、NaN
は浮動小数点数float
型なので、NaN
が存在する列のデータ型dtype
はfloat
になる。
ずらし幅を指定: 引数periods
第一引数periods
にずらし幅を指定する。デフォルトはperiods=1
。
print(df.shift(2))
# a b c
# 0 NaN NaN NaN
# 1 NaN NaN NaN
# 2 1.0 1.0 1.0
# 3 2.0 4.0 8.0
# 4 3.0 9.0 27.0
負の値もOK。上方向にずらされる。
print(df.shift(-1))
# a b c
# 0 2.0 4.0 8.0
# 1 3.0 9.0 27.0
# 2 4.0 16.0 64.0
# 3 5.0 25.0 125.0
# 4 NaN NaN NaN
ずらす方向を指定: 引数axis
デフォルトでは縦方向にずらされる。引数axis=1
とすると横方向にずらされる。
print(df.shift(axis=1))
# a b c
# 0 NaN 1.0 1.0
# 1 NaN 2.0 4.0
# 2 NaN 3.0 9.0
# 3 NaN 4.0 16.0
# 4 NaN 5.0 25.0
print(df.shift(-1, axis=1))
# a b c
# 0 1.0 1.0 NaN
# 1 4.0 8.0 NaN
# 2 9.0 27.0 NaN
# 3 16.0 64.0 NaN
# 4 25.0 125.0 NaN
時系列データに対するshift()
以下の時系列データを例とする。
import pandas as pd
df = pd.DataFrame({'value': range(1, 16, 2)},
index=pd.date_range('2018-01-01', '2018-01-15', freq='2D'))
print(df)
# value
# 2018-01-01 1
# 2018-01-03 3
# 2018-01-05 5
# 2018-01-07 7
# 2018-01-09 9
# 2018-01-11 11
# 2018-01-13 13
# 2018-01-15 15
これまでの例と同様に第一引数periods
に数値を指定して行単位でずらすことが可能。
print(df.shift())
# value
# 2018-01-01 NaN
# 2018-01-03 1.0
# 2018-01-05 3.0
# 2018-01-07 5.0
# 2018-01-09 7.0
# 2018-01-11 9.0
# 2018-01-13 11.0
# 2018-01-15 13.0
ずらす期間を指定: 引数freq
時系列データに対しては、引数freq
でずらす期間を指定できる。
引数freq
にはD
(日)、H
(時)などの頻度コードを指定する。頻度コードについては以下の記事を参照。
引数freq
を指定した場合、データはそのままでインデックス列の値が変わる。
print(df.shift(freq='D'))
# value
# 2018-01-02 1
# 2018-01-04 3
# 2018-01-06 5
# 2018-01-08 7
# 2018-01-10 9
# 2018-01-12 11
# 2018-01-14 13
# 2018-01-16 15
例えば3日分ずらしたい場合は、freq='3D'
としてもいいし、periods=3, freq='D'
としてもOK。
print(df.shift(freq='3D'))
# value
# 2018-01-04 1
# 2018-01-06 3
# 2018-01-08 5
# 2018-01-10 7
# 2018-01-12 9
# 2018-01-14 11
# 2018-01-16 13
# 2018-01-18 15
print(df.shift(3, freq='D'))
# value
# 2018-01-04 1
# 2018-01-06 3
# 2018-01-08 5
# 2018-01-10 7
# 2018-01-12 9
# 2018-01-14 11
# 2018-01-16 13
# 2018-01-18 15
注意点
W
やM
は注意が必要。W
の場合は次の日曜日、M
の場合は次の月末にインデックスの値が変わる。1週間分あるいは1ヶ月分ずれるわけではない。
print(df.shift(freq='W'))
# value
# 2018-01-07 1
# 2018-01-07 3
# 2018-01-07 5
# 2018-01-14 7
# 2018-01-14 9
# 2018-01-14 11
# 2018-01-14 13
# 2018-01-21 15
print(df.shift(freq='M'))
# value
# 2018-01-31 1
# 2018-01-31 3
# 2018-01-31 5
# 2018-01-31 7
# 2018-01-31 9
# 2018-01-31 11
# 2018-01-31 13
# 2018-01-31 15
1週間分あるいは1ヶ月分ずらしたい場合は'7D'
や'31D'
などを指定する。
print(df.shift(freq='7D'))
# value
# 2018-01-08 1
# 2018-01-10 3
# 2018-01-12 5
# 2018-01-14 7
# 2018-01-16 9
# 2018-01-18 11
# 2018-01-20 13
# 2018-01-22 15
print(df.shift(freq='31D'))
# value
# 2018-02-01 1
# 2018-02-03 3
# 2018-02-05 5
# 2018-02-07 7
# 2018-02-09 9
# 2018-02-11 11
# 2018-02-13 13
# 2018-02-15 15
なお、元のデータが月末の日付である場合はfreq='M'
としても問題ない。
df_m = pd.DataFrame({'value': range(1, 13)},
index=pd.date_range('2018-01-01', '2018-12-31', freq='M'))
print(df_m)
# value
# 2018-01-31 1
# 2018-02-28 2
# 2018-03-31 3
# 2018-04-30 4
# 2018-05-31 5
# 2018-06-30 6
# 2018-07-31 7
# 2018-08-31 8
# 2018-09-30 9
# 2018-10-31 10
# 2018-11-30 11
# 2018-12-31 12
print(df_m.shift(freq='M'))
# value
# 2018-02-28 1
# 2018-03-31 2
# 2018-04-30 3
# 2018-05-31 4
# 2018-06-30 5
# 2018-07-31 6
# 2018-08-31 7
# 2018-09-30 8
# 2018-10-31 9
# 2018-11-30 10
# 2018-12-31 11
# 2019-01-31 12