dplyrを拡張してデータフレームっぽい俺俺クラスを動かす
みんな大好きデータフレームのハンドリング用ライブラリdplyrを拡張して、時系列データのxts型も同じように操作できるtplyrパッケージを作ろうと思ったんだが、よくよく考えるとdplyrままでいいんじゃないかなって思って辞めた。
その際に試行錯誤した残骸を忘れないようにここにまとめる。
ここでは、dplyrのfilter関数に対してxts型が動作するように拡張することを考えよう。
そのために、以下のように3つの関数を用意する。
内容はコメントにあるとおりxtsとdata.frameの変換関数とxtsに対する実際のfilter処理だ。
# xtsをdata.frameへと変換する関数 xts_to_df <- function(x) { data.frame(index=zoo::index(x), zoo::coredata(x)) } # data.frameをxtsへと変換する関数 df_to_xts <- function(df) { xts(dplyr::select(df, -index), order.by=df$index) } # xts型に対するfilter処理を記述(S3クラスのメソッドとする) filter_.xts <- function(.data, ..., .dots) { dots <- lazyeval::all_dots(.dots, ...) df <- xts_to_df(.data) df_to_xts(dplyr::filter_(df, .dots = dots)) }
そして、ここがミソだが、ここで作成したfilter_.xts関数を無理やりdplyrのnamespaceに突っ込む。
こうすることで、dplyrパッケージの中の処理において、xts型のfilter処理に対してこの関数を呼んでくれるようになる*1。
environment(filter_.xts) <- asNamespace("dplyr")
さて、実際に動くかxtsパッケージにあるサンプルデータで確かめてみよう。
> library(xts) > data(sample_matrix) > # 適当なxtsデータへと変換 > samplexts <- as.xts(sample_matrix) > # 内容の確認 > head(samplexts) Open High Low Close 2007-01-02 50.03978 50.11778 49.95041 50.11778 2007-01-03 50.23050 50.42188 50.23050 50.39767 2007-01-04 50.42096 50.42096 50.26414 50.33236 2007-01-05 50.37347 50.37347 50.22103 50.33459 2007-01-06 50.24433 50.24433 50.11121 50.18112 2007-01-07 50.13211 50.21561 49.99185 49.99185 > # ちゃんと動いてるか確認 > dplyr::filter(samplexts, Close > 51) Open High Low Close 2007-02-14 50.95283 51.04699 50.80317 51.04699 2007-02-15 51.06330 51.11401 50.94681 51.05185 2007-02-16 51.12879 51.12879 51.00613 51.02164 2007-02-17 50.97722 51.13653 50.95260 51.13653 2007-02-18 51.18414 51.32090 51.13713 51.15151 2007-02-19 51.29502 51.32342 51.13524 51.17899
・・・ちゃんとfilter処理できてる!やったぜ、親分!
*1:と思ったが、グローバル環境に定義してる分にはこれは不要で、パッケージ化してこれをやろうとすると必要になるんだった、失敬失敬。