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氏から以下のようなアドバイスが!!!

これを試すと・・・

> 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"

バッチリきた!!これだ!!