sapplyの戻り値がmatrix型に固定できない私はlapply+Reduce&cbindでしのぐという決断を下したのですが、mutable_eachを使えば良いということがわかりました。
やりたいことへの試行錯誤
例えばdplyrとmagrittrを活用して、mtcarsデータセットのmpg, cylの両列に対してround関数を適用するには、以下のように書くわけです。
> library(dplyr) > mtcars %>% select(mpg, cyl) %>% sapply(round) mpg cyl [1,] 21 6 [2,] 21 6 [3,] 23 4 [4,] 21 6 [5,] 19 8 ・・・省略・・・
この結果は行列として返却されてきます。
> mtcars %>% select(mpg, cyl) %>% sapply(round) %>% class [1] "matrix"
一方、同じシチュエーションにおいて、データが1つしかない場合、
> mtcars %>% select(mpg, cyl) %>% head(1) %>% sapply(round) mpg cyl 21 6
となって、結果は良さそうに見えるが、これだと出力はmatrixではなく数値のベクトルになってしまう。
> mtcars %>% select(mpg, cyl) %>% head(1) %>% sapply(round) %>% class [1] "numeric"
これだと
- データがたくさんある時は行列
- データが1つの時はベクトル
とデータの個数に応じてデータ構造が揃っておらず、使い勝手が悪い。
当然、データが1つのケースに対して、無理矢理t関数やらかませてmatrixにもっていくことは出来るんだけど、それだと通常のデータ処理系の方で矛盾が生じてしまって、これもまた都合がわるいぞと。
これを解消するために、sapply関数を使うのをやめて、lapply + Reduce, cbindで処理を書いた。
> mtcars %>% select(mpg, cyl) %>% head(1) %>% lapply(round) %>% Reduce(cbind, .) init [1,] 21 6
結果は同じでしかも、ちゃんとmatrixになる。
> mtcars %>% select(mpg, cyl) %>% head(1) %>% lapply(round) %>% Reduce(cbind, .) %>% class [1] "matrix"
これはデータが1つじゃないときもちゃんと動作する。
> mtcars %>% select(mpg, cyl) %>% lapply(round) %>% Reduce(cbind, .) init [1,] 21 6 [2,] 21 6 [3,] 23 4 [4,] 21 6 [5,] 19 8 ・・・省略・・・
列名がなくなってしまったが、それはsetNames関数を噛ませてなんとかする方向でいいや。
結論
- データフレームの列毎に処理を適用したい時は、lapply+Reduce(cbind)使う
一時しのぎ的なんでもっといいアイディア欲しい。
・・・からのmutable_each
みんなのデータサイエンティスト@sfchaos氏から以下のようなアドバイスが!!!
@teramonagi @dichika 題意を取り違えていました・・・ roundに限らず,一般的な列ごとの演算ですね.これでいけるはず.mtcars %>% select(mpg, cyl) %>% mutate_each(funs(round))
— sfchaos (@sfchaos) 2014, 8月 10
これを試すと・・・
> mtcars %>% select(mpg, cyl) %>% head(1) %>% mutate_each(funs(round)) mpg cyl 1 21 6 > class(mtcars %>% select(mpg, cyl) %>% head(1) %>% mutate_each(funs(round))) [1] "data.frame"
バッチリきた!!これだ!!