追記('15/01/04):*_join()
の図を追加しました。
新年早々、dplyrの新バージョンがリリースされるらしいです。
dplyr/NEWS.md at master · hadley/dplyr · GitHub
新機能についてはHadleyさんがRPubsに書いてるので、もうブログ書かなくてもいいかなーと思いつつ、修行のため使ってみます。
インストール
まだCRANには来てないのでinstall_github()
します。
devtools::install_github("dplyr")
バージョン確認。
> packageVersion("dplyr") [1] ‘0.4.0’
add_rownames()
add_rownames()
turns row names into an explicit variable (#639).
rowname
をdata.frame
の列として扱うようにできます。
これがある理由
RPubs - dplyr-0.4によると、dplyrではrowname
を使うのは推奨されていなくて、その理由は
no dplyr method is guaranteed to preserve them
というのと、
I don’t think using row names is a good idea because it violates one of the principles of tidy data: every variable should be stored in the same way.
ということらしいです。
使ってみる
こんなデータがあるとします。
> (a <- data.frame(x = 1:6, y = runif(6), row.names = letters[1:6]) ) x y a 1 0.1704538 b 2 0.2886246 c 3 0.6670413 d 4 0.4474072 e 5 0.6183439 f 6 0.2718875
これをadd_rownames()
を通すとこうなります。
> add_rownames(a, var = "行名") x y 行名 1 1 0.1704538 a 2 2 0.2886246 b 3 3 0.6670413 c 4 4 0.4474072 d 5 5 0.6183439 e 6 6 0.2718875 f
filter()
するときとか便利そうです。
> a %>% + add_rownames("rn") %>% + filter(rn == "c") x y rn 1 3 0.6670413 c
as_data_frame()
as_data_frame()
efficiently coerces a list into a data frame (#749).
これはRPubs - dplyr-0.4を見てると、as.data.frame()
の爆速バージョン、みたいな感じらしいです。コードを見てると、
ということをやっているようです。結果として得られるものはあんまり変わらなさそうなので説明は省きます。list
の扱いはlowliner
を使ってもっとよくなるよ、的なことが仄めかされているのでdplyr 0.5のときにはもっと便利になっているかもしれません。
bind_rows()
, bind_cols()
, combine()
bind_rows()
andbind_cols()
efficiently bind a list of data frames by row or column.combine()
applies the same coercion rules to vectors (it works likec()
orunlist()
but is consistent with thebind_rows()
rules).
bind_rows()
rbind()
の代わり。すでに、rbind_list()
とかrbind_all()
がありますが、bind_rows()
を使うと引数のデータ形式で悩むことが減ります。
こういうデータがあるとします。
> a <- split(mtcars, mtcars$cyl) > str(a, max.level = 1) List of 3 $ 4:'data.frame': 11 obs. of 11 variables: $ 6:'data.frame': 7 obs. of 11 variables: $ 8:'data.frame': 14 obs. of 11 variables:
rbind_list()
とかrbind_all()
だと、引数の形を間違えるとエラーが出たりします。(正しくは、rbind_all()
はdata.frame
のlist
オブジェクトをひとつだけ引数に取ります。rbind_list()
はdata.frame
オブジェクトを複数引数に取ります)
> rbind_all(a[[1]], a[[2]]) Error in rbind_all(a[[1]], a[[2]]) : unused argument (a[[2]]) > rbind_list(a) Source: local data frame [0 x 0]
bind_rows()
はどっちでも大丈夫です。(内部でやってるのはデータ形式を整えてrbind_all()
を呼び出すだけのようです)
> bind_rows(a) %>% head(3) Source: local data frame [3 x 11] mpg cyl disp hp drat wt qsec vs am gear carb 1 22.8 4 108.0 93 3.85 2.32 18.61 1 1 4 1 2 24.4 4 146.7 62 3.69 3.19 20.00 1 0 4 2 3 22.8 4 140.8 95 3.92 3.15 22.90 1 0 4 2 > bind_rows(a[[1]], a[[2]]) %>% head(3) Source: local data frame [3 x 11] mpg cyl disp hp drat wt qsec vs am gear carb 1 22.8 4 108.0 93 3.85 2.32 18.61 1 1 4 1 2 24.4 4 146.7 62 3.69 3.19 20.00 1 0 4 2 3 22.8 4 140.8 95 3.92 3.15 22.90 1 0 4 2
bind_cols()
cbind()
の代わり。do.call(cbind, list(data.frame1, data.frame2,..))
とやるのと同じみたいです。
> bind_cols(data.frame(a = 1:3, b = 11:13), data.frame(c = letters[1:3], d = LETTERS[1:3])) a b c d 1 1 11 a A 2 2 12 b B 3 3 13 c C
が、Hadleyさんは、列の順番が意図通りになってないこともあるから*_join()
を使った方がいいよ、と言ってます。
(Generally you should avoid
bind_cols()
in favour of a join; otherwise check carefully that the rows are in a compatible order).
combine()
c()
とかunlist()
の代わり。
ヘルプの例に書いてあるのをやってみます。c()
とかunlist()
はfactorをかってに数字にしてしまったり(この挙動なやましいですよね)、levelを合成してしまったりします。
> f1 <- factor("a") > f2 <- factor("b") > c(f1, f2) [1] 1 1 > unlist(list(f1, f2)) [1] a b Levels: a b
combine()
はcharacter型に直してくれます。いい感じ。
> combine(f1, f2) [1] "a" "b" > combine(list(f1, f2)) [1] "a" "b"
ただしその分引数のチェックが少し厳しくなっていて、c()
とかunlist()
とかにはnumericとcharacterを混ぜて入れても大丈夫ですが、combine()
だとエラーが出ます。(これで困る人あんまりいない気もしますけど)
> unlist(list(1, "character")) [1] "1" "character" > combine(list(1, "character")) Error: incompatible type at index 2 : character, was collecting : numeric
right_join()
,full_join()
right_join()
(include all rows iny
, and matching rows inx
) andfull_join()
(include all rows inx
andy
) complete the family of mutating joins (#96).
これで*_join()
のファミリーがそろったことになります。two-tableというVignetteにSQLとの対照表があります。
Visual Representation of SQL Joins - CodeProjectに触発されて図にしてみたので貼っておきます。
group_indices()
group_indices()
computes a unique integer id for each group (#771). It can be called on a grouped_df without any arguments or on a data frame with same arguments asgroup_by()
.
ユニークなグループIDを振ってくれます。
これいまいち使い方がピンときてないんですけど、何度もgroup_by()
するときは、
mtcars %>% mutate_(., group1 = group_indices(., cyl, vs, am), group2 = group_indices(., cyl, gear, carb) ) -> grouped_mtcars
とかやっておくと、いちいち毎回グループ分けに使う変数名のリストを渡さなくても
group_by(grouped_mtcars, group1) group_by(grouped_mtcars, group2)
とかできて便利、とかでしょうか?(詳しい方、教えてください...)
print()
ここから先はMinor improvementsの中から気になったところだけ。
print()
まわりの改善は、NEWSではminor扱いですが、RPubs - dplyr-0.4には詳しく書いています。
たとえば、
All
print()
method methods invisibly return their input so you can interleaveprint()
statements into a pipeline to see interim results.
つまり、こんな感じで結果を表示しつつパイプをつないでいくことができます。
mtcars %>% print %>% group_by(cyl) %>% print %>% summarize(mean(mpg))
これまでも、magrittrの%T>%
を使うとできましたが(参考:magrittr 1.5を試してみる。 - Technically, technophobic.)、これデバッグの時に便利そうですね。
do_()
do()
のnon-standard evaluation版。
slice()
data.tableも動くようになったらしいです。
RDBの場合は動かないので、filter()
で同じことをする方法がヘルプに書いてあります。
# Equivalent code using filter that will also work with databases, # but won't be as fast for in-memory data. For many databases, you'll # need to supply an explicit variable to use to compute the row number. filter(mtcars, row_number() == 1L) filter(mtcars, row_number() == n()) filter(mtcars, between(row_number(), 5, n()))
感想
そんなに劇的に変わった部分はなさそうな印象です。ちょこちょこ便利になってるっぽいですが、なんか細かいのが多くて追いきれませんでした。
RPubs - dplyr-0.4にはちらほらdplyr 0.5のことも仄めかされていて、今後の方向性が気になるところです。
個人的には一番最後のこのへんが気になっています。lowliner
使ってみようかな。
My vision of list-variables is still partial and incomplete, but I’m convinced that they will make pipeable APIs for modelling much eaiser. See the draft lowliner package for more explorations in this direction.