Pandas のshift を使うと、現在の行の値と前後の行の値を比較できる。
以前SQLの分析関数であるLAGとLEADの動きを確認したが、Pandasではどのように実現していくのか見ていくものになる。LAG関数とLEAD関数も比較して見てもらえると!
実行環境
今回は環境構築がいらないGoogle Colaboratoryを使った。
準備と確認
まずはPandasのインポートと、はじめに使用するデータを用意する。 データはLAG関数で確認したものと同じものにした。
import pandas as pd dict1=dict(id=[1,2,3], Money=[1000,2000,3000], day=['2021-01-01','2021-02-01','2021-03-01']) df1 = pd.DataFrame(data=dict1) df1
id Money day 0 1 1000 2021-01-01 1 2 2000 2021-02-01 2 3 3000 2021-03-01
shiftを実行する
そのままshift
関数を実行し結果を確認する。
df1.shift()
id Money day 0 NaN NaN NaN 1 1.0 1000.0 2021-01-01 2 2.0 2000.0 2021-02-01
ここでわかることは、1行まるごとshiftされていることである。 id列、Money列、day列全てが1行分ズレていている。 単純に実行しただけではSQLのLAG関数のように「特定列だけズレる」ようには動いてくれないことがわかった。
特定の列だけズラす
特定の列だけズラすために、データを変更する。 変更としては、
- idとMoneyを6個分増やす
- day列を削除し、代わりの日付データをインデックスとして再定義した。
shift
関数へもパラメータを設定する変更を加えている。
今回はperiodsとfreqを指定する。
公式ドキュメントからDeepL翻訳したものを掲載する。 periodsはLAG関数のoffsetと同じもので、ズラす数を書く。1と書けば1つズレるし、3と書けば3つ分ズレることになる。
periods int シフトする期間の数。正または負を指定できます。
freqはLAG関数には無いものになる。 時系列データに対して、freqでずらす期間を指定できる。 コードの中ではDを指定していて、日単位でズラすようにしている。
freq DateOffset, tseries.offsets, timedelta, or str, optional tseriesモジュールまたはタイムルールから使用するオフセット(例:'EOM')。freqが指定された場合、インデックス値はシフトされますが、データは再調整されません。つまり、シフト時にインデックスを拡張し、>元のデータを維持したい場合は、freqを使用します。freqが "infer "として指定された場合は、インデックスのfreqまたはinferred_freq属性から推測されます。これらの属性が存在しない場合、ValueErrorが投げられます。
実行しているコードにコメントを差し込んでいるので、実際に何をコードを確認してほしい。
# ディクショナリでidとMoneyをdict2変数に定義 dict2=dict(id=[1,2,3,4,5,6], Money=[1000,2000,3000,4000,5000,6000]) # date_range関数で2021-01-01から6日分をday変数に格納 day=pd.date_range("2021-01-01", periods=6) # dict2とdayを元にDataFrameを作成 df2 = pd.DataFrame(data=dict2,index=day) # periodsで1ずつ、さらにfreq="D"で日付ごとズラすことを指定し、Money列の値を取得、lag列に入れる df2['lag'] = df2.shift(periods=1,freq="D")['Money'] df2
id Money lag 2021-01-01 1 1000 NaN 2021-01-02 2 2000 1000.0 2021-01-03 3 3000 2000.0 2021-01-04 4 4000 3000.0 2021-01-05 5 5000 4000.0 2021-01-06 6 6000 5000.0
グループ内でshiftする
LAG関数はpartition by
でグループ化したものをズラすことが出来た。
こちらもshift
関数では少し工夫が必要になる。
データはDate列を戻し、グループ化させたいので下記データを2つずつ用意した。
- 2021-01-01
- 2021-02-01
- 2021-03-01
再びコードの中で何をしているかはコメントを差し込んでいるので確認してほしい。
# ディクショナリでidとMoneyとDateをdict3変数に定義 dict3=dict(id=[1,2,3,4,5,6], Money=[1000,2000,3000,4000,5000,6000], Date=['2021-01-01','2021-01-01','2021-02-01','2021-02-01','2021-03-01','2021-03-01']) # dict3を元にDataFrameを作成 df3 = pd.DataFrame(data=dict3) # df3のDate列を対象にgroupbyを実行、さらにMoney列をshiftしたものをlag列に入れる df3['lag'] = df3.groupby(['Date'])['Money'].shift() df3
無事にpartition byと同じことが出来ている事がわかる。
id Money Date lag 0 1 1000 2021-01-01 NaN 1 2 2000 2021-01-01 1000.0 2 3 3000 2021-02-01 NaN 3 4 4000 2021-02-01 3000.0 4 5 5000 2021-03-01 NaN 5 6 6000 2021-03-01 5000.0
NaNの代わりに値を設定する
shift(fill_value=0)
のようにパラメータのfill_valueを使うと、NaNにデフォルトの値としてセットすることができる。
今回は0を設定している。
dict3=dict(id=[1,2,3,4,5,6], Money=[1000,2000,3000,4000,5000,6000], Date=['2021-01-01','2021-01-01','2021-02-01','2021-02-01','2021-03-01','2021-03-01']) df3 = pd.DataFrame(data=dict3) df3['lag'] = df3.groupby(['Date'])['Money'].shift(fill_value=0) df3
id Money Date lag 0 1 1000 2021-01-01 0 1 2 2000 2021-01-01 1000 2 3 3000 2021-02-01 0 3 4 4000 2021-02-01 3000 4 5 5000 2021-03-01 0 5 6 6000 2021-03-01 5000
まとめ
shift関数を使いながら動きを確認できた。 LEAD 関数のように使いたい場合はshift(-1)
のようにマイナス値を設定すると上にずれてくれる。
dict4=dict(id=[1,2,3,4,5,6], Money=[1000,2000,3000,4000,5000,6000], Date=['2021-01-01','2021-01-01','2021-02-01','2021-02-01','2021-03-01','2021-03-01']) df4 = pd.DataFrame(data=dict4) df4['lag'] = df4.groupby(['Date'])['Money'].shift(-1,fill_value=0) print(df4)
id Money Date lag 0 1 1000 2021-01-01 2000 1 2 2000 2021-01-01 0 2 3 3000 2021-02-01 4000 3 4 4000 2021-02-01 0 4 5 5000 2021-03-01 6000 5 6 6000 2021-03-01 0
そのまま手を動かせるものにしたので、実行して確認してもらえれば👌
👇SQLのLAG関数とLEAD関数はこちら👇