【R備忘】{slider}パッケージの主要関数メモ
師走だけど、走らないよ
Introduction
時系列データ(あるいはもっと一般に、sequenceのindexがあるデータ)をあつかうとき、いわゆる「window関数」を定義して各指標を計算する(例:移動平均)ことは多い。
{slider}は、そのwindow関数による処理をより簡単におこなうためのパッケージである
なんか便利そうなので、主要関数の使い方をまとめておく
◆ 参考URL
関数ごとの使い方
slide()
基本関数。
- .x(第一引数)にデータ(ベクトルやdata frame)を指定
- .f(第二引数)に、関数を指定("~.x ** 2 "みたいに、表現式での指定でも可能
- .before/.after引数には、前後何個分のデータを取得するかを指定。際限ない場合はInfにする
- slide()は"type-stable"であり、取得したい値、.fに指定する関数の戻り値によってslide_dbl, slide_int, slide_dfr(データフレームね)などを使いましょう、とのこと。slide()はlist型を返す。
- 他には.step(ステップ数を指定)、.complete(boolean; 系列に欠損がある場合も演算するか否かを指定)などの引数があり
引数名の「.」を忘れがちなので、注意
slide(1:4 , ~.x, .before=1 , .after=1) # [[1]] # [1] 1 2 #.complete=TならここはNULLになる # # [[2]] # [1] 1 2 3 # # [[3]] # [1] 2 3 4 # # [[4]] # [1] 3 4 #.complete=FならここはNULLになる slide_dbl(1:5 ,.f = ~mean(.x), .before=1 , .after=1) # [1] 1.5 2.0 3.0 4.0 4.5 #_dblにしたのでnumericのベクトルで平均がかえってくる
slide_index()
slide()では、windowの幅を指定するさいに、系列はすべて等間隔で並んでいるという想定が暗黙裡になされていた
slide_indexは()、日付や時間、その他数値などの変数で系列上の位置が与えられているとき、その変数の値を利用してwindow幅を指定することができるのである。
たとえば、名古屋グランパス2022シーズンnの最後10試合を抽出すると、代表weekがあったりしたので
以下のように試合ごとの時間間隔がかなりマチマチになっていることがわかる。
こういうときに、過去N試合じゃなくて、「過去1か月」の平均得点を知りたい・計算したいときは、slide_index()が役に立つ
library( lubridate) #日付をあつかうための有能package #名古屋のラスト10試合を抽出 nge_last <- dfJ22 %>% filter( Club == "nago") %>% arrange( Date) %>% tail(10) ## 過去1か月の対戦相手をとってくる ## slide_index( .x = nge_last$Opponent, .i = nge_last$Date, .f = ~.x, .before = months(1)) # # [[1]] # [1] "iwat" # # [[2]] # [1] "iwat" "g-os" # # [[3]] # [1] "iwat" "g-os" "fuku" # # [[4]] # [1] "iwat" "g-os" "fuku" "kobe" # # [[5]] # [1] "iwat" "g-os" "fuku" "kobe" "ka-f" # # [[6]] # [1] "iwat" "g-os" "fuku" "kobe" "ka-f" "hiro" ## 「過去1か月の得点点の平均」を数値ベクトルとして取得 ## slide_index_dbl( .x = nge_last$Goal_For, .i = nge_last$Date, .f = mean, .before = months(1)) # [1] 1.0000000 0.5000000 1.3333333 1.0000000 1.0000000 # [6] 0.8333333 0.8000000 0.4000000 1.0000000 1.3333333
まぁ例があまり適切じゃなかったけど、たとえば追跡パネルみたいなデータがあって途中離脱→復帰みたいなサンプルが含まれているときに、
単なるラグ変数をとるとおかしくなったりするので、そういうときに便利なのではなかろうか。
slide_period()
slide_indexと似てるんだけど、非なるもの。
indexとなる変数をより上位の単位にまとめて、その単位でwindowをつくる。
例)日付をIndexにして、「その月」の平均売上を算出する
一瞬、floor_date() + group_by()でよくね?ってなるけど、「当月+前月」みたいな感じで、
以下のように月単位でのwindow指定がえらい簡単にできるところに優位性があるよ、とのこと
# Vignetteからの抜粋 monthly_summary <- function(data) { summarise(data, index = max(index), sales = sum(sales)) } slide_period_dfr( big_company, big_company$index, "month", monthly_summary ) #> # A tibble: 4 × 2 #> index sales #> <date> <dbl> #> 1 2019-08-31 6 #> 2 2019-09-03 16 #> 3 2019-11-30 10 #> 4 2019-12-04 19 ## 例:当月&前月 slide_period_dfr( big_company, big_company$index, "month", monthly_summary, .before = 1 ) #> # A tibble: 4 × 2 #> index sales #> <date> <dbl> #> 1 2019-08-31 6 #> 2 2019-09-03 22 #> 3 2019-11-30 10 #> 4 2019-12-04 29
三番目の引数(.period)がまとめる単位になるんだけど、そこに指定できるのは以下の文字列。
分単位の時刻データを時間単位にまとめる、とかもできる。
※"yday"とか"mday"とかはじめてきいたけど、わかりやすく解説されている記事があった。月初めや年はじめからの起算日数と。
tidyな処理との組み合わせなど
我々が扱うデータというのはしばしば単一系列ではなく、複数のユニットに紐づく複数系列だったりする。
各支社の売上の系列だったり、各国の気温変動だったり、各クラブの得点だったり...という具合にだ。
そういうときには、tidyverseの諸々の便利関数と組み合わせる。
ためしに、J1の2022年でデータで、クラブごとに「前後10日の平均勝点」を各節時点で求めてみる。
dfJ22_2 <- dfJ22 %>% group_by( Club) %>% #クラブごとにグループ化 arrange( Date) %>% #日付昇順にする nest() %>% #nestする mutate( Goal_For_M = map( data , ~ slide_index_dbl(.x = .$Goal_For , .i = .$Date , .f = mean , .before = days(10), .after= days(10))) ) %>% select(-data) %>% unnest( Goal_For_M) %>% as.data.frame() head(dfJ22_2) ## 名古屋のラスト10試合で確認 dfJ22_2 %>% filter( Club=="nago") %>% tail(10) %>% bind_cols( nge_last %>% select( -Club)) %>% select( Club , Date , Goal_For , Goal_For_M) # Club Date Goal_For Goal_For_M # 1 nago 2022-08-19 1 0.3333333 # 2 nago 2022-08-27 0 1.3333333 # 3 nago 2022-09-03 3 1.0000000 # 4 nago 2022-09-10 0 1.0000000 # 5 nago 2022-09-14 1 0.3333333 # 6 nago 2022-09-17 0 0.3333333 # 7 nago 2022-10-01 0 0.5000000 # 8 nago 2022-10-08 1 0.5000000 # 9 nago 2022-10-29 2 1.5000000 # 10 nago 2022-11-05 1 1.5000000
たとえば9/10のところには、きちんと前後10日(3+0+1+0)の平均(1.0点)が入っており、求める挙動になっている。
だいたい使い方は以上である。
2020年リリースということで、COVID-19下で脚光を浴びたパッケージらしいんですが、普通に縦断データの分析とかをするのに便利だと思いました
Enjoy!!