dplyr 0.5.0を使ってみる
※この記事は4/9に書いた「dplyr 0.4.3.9000を使ってみる」という記事を加筆したものです
「1か月くらいしたら新しいdplyr出るよ」とHadleyが言ってました。
Getting ready to start dplyr release process. Over >150 issues fixed: https://t.co/N6fOAnN5pF. Expect on CRAN in ~1 month #rstats
— Hadley Wickham (@hadleywickham) March 24, 2016
それから1か月が過ぎ、2か月が過ぎ、3か月が過ぎ、、、ようやくリリースされました! 長かった。。
Release dplyr 0.5.0 · hadley/dplyr · GitHub
変更点を見てみます。
Breaking changes
arrange()
はグループを無視
arrange()
once again ignores grouping (#1206)
group_by()
に指定したグループがarrange()
で無視されるようになりました。
このx
もソートするキーに指定したい場合は、↓のような感じで明示的に指定する必要があります。
d <- data.frame(x = c("a", "a", "b", "b"), y = 1:4) d %>% group_by(x) %>% arrange(x, y)
distinct()
は引数に指定した列しか返さない
distinct()
now only keeps the distinct variables. If you want to return all variables (using the first row for non-distinct values) use.keep_all = TRUE
(#1110).
select()
のヘルパー関数がエクスポートされた
The select helper functions
starts_with()
,ends_with()
etc are now real exported functions. This means that you'll need to import those functions if you're using from a package where dplyr is not attached.
select()
の中だけで動いていた関数がちゃんとエクスポートされるようになりました。このため、dplyr::select()
単体で呼び出す時はstarts_with()
にもdplyr::
をつけないとエラーになったりするので注意。
deprecatedになった関数
The long deprecated
chain()
,chain_q()
and%.%
have been removed. Please use%>%
instead.
id()
has been deprecated. Please usegroup_indices()
instead. (#808)
rbind_all()
andrbind_list()
are formally deprecated. Please usebind_rows()
instead (#803).
Outdated benchmarking demos have been removed (#1487).
Code related to starting and signalling clusters has been moved out to multidplyr.
rbind_all()
とrbind_list()
は正式にdeprecatedになりました。(以前はbind_rows()
の中で使われていましたが、今はもう違う実装になっています)
New functions
coalesce()
coalesce()
finds the first non-missing value from a set of vectors. (#1666, thanks to @krlmlr for initial implementation).
ベクトルを順番に見ていって、はじめに見つかったNA
でない要素を返します。
x <- c(1, NA, NA, NA) y <- c(2, 2, NA, NA) z <- c(3, 3, 3, NA) coalesce(x, y, z) #> [1] 1 2 3 NA
と書くと具体的な利用シーンがよく分かりませんが、これはNA
を置き換えるという用途が念頭にあるようです。
こんな感じでいちばん最後に長さ1のベクトルを指定すると、その値でNA
をうめることができます。
coalesce(x, 0) #> [1] 1 0 0 0
case_when()
case_when()
is a general vectorised if + else if (#631).
これはベクトル化されたswitch()
みたいなやつです。書き方がちょっと独特ですが、表現式 ~ 値
を並べていくというスタイルです。
一つ注意点としては、パイプの中ではうまく動きません。(参考:Equivalent of SQL CASE · Issue #631 · hadley/dplyr · GitHub)
mtcars %>% mutate( size = case_when( cyl > 6 ~ "big", TRUE ~ "small" )) Error: object 'cyl' not found
.$
をつければアクセスできるんですが、ちょっと分かりづらい。。
size = case_when( .$cyl > 6 ~ "big", TRUE ~ "small" )
ただ、簡単なやつは下に出てくるif_else()
とかrecode()
でできます。この2つはパイプの中で使えるので、case_when()
が必要になる場合は少ないかも。
if_else()
if_else()
is a vectorised if statement: it's a stricter (type-safe), faster, and more predictable version ofifelse()
. In SQL it is translated to aCASE
statement.
これほしかったやつ!!
ifelse()
がつらいのは以下の2点です。
- 型が勝手に変わる(参考:ifelse()のつらみ アップデート版 - Technically, technophobic.)
- dplyrがSQLのCASEにうまく変換してくれない(参考:dplyr で DB にクエリを投げる時に CASE 式を使いたい #rstatsj - Qiita)
if_else()
はこのいずれもが解消されていてしかも速いということなので、使わない理由はないでしょう。
na_if()
na_if()
makes it easy to replace a certain value with anNA
(#1707). In SQL it is translated toNULL_IF
.
これはcoalesce()
の逆です。ある値をNA
と置き換える関数です。たとえば元データでNA
は"N/A"
となっている場合は、
sweets <- c("geppei", "manju", "N/A", "sakuramochi", "N/A") na_if(sweets, "N/A") #> [1] "geppei" "manju" NA "sakuramochi" NA
のようにすれば"N/A"
をNA
で置き換えられます。
near()
near(x, y)
is a helper forabs(x - y) < tol
(#1607).
これはall.equal()
と同じやつだと思います。計算誤差の許容範囲なら同じと判定する関数です。
2 == sqrt(2)^2 #> [1] FALSE near(2, sqrt(2)^2) #> [1] TRUE
recode()
recode()
is vectorised equivalent toswitch()
(#1710).
これもほしかったやつです。switch()
は長さ1のものにしか使えませんでしたが、recode()
はベクトルに対して使うことができます。
x <- c("a", "b", "a", "b", "a", "c") recode(x, a = "あ", .default = "うん") #> [1] "あ" "うん" "あ" "うん" "あ" "うん"
union_all()
union_all()
method. Maps toUNION ALL
for SQL sources,bind_rows()
for data frames/tbl_dfs, andcombine()
for vectors (#1045).
バックエンドの種類によって適切な関数を使い分けてくれるラッパーみたいです。
summarise
とmutate
関数のシリーズに_all()
,_if()
,_at()
が入った
A new family of functions replace
summarise_each()
andmutate_each()
(which will thus be deprecated in a future release)
select_if()
lets you select columns with a predicate function. Only compatible with local sources.
これはけっこう大きな変更です。
たとえば、これまでのmutate_each()
では、
iris %>% group_by(Species) %>% summarise_each(funs(mean), starts_with("S"))
と書いていたものが、
iris %>% group_by(Species) %>% summarise_at(vars(starts_with("S")), funs(mean))
と書くようになります。なんでこんなめんどくさい記法になったかというと、対象カラムの選択をもっと柔軟にできるようにするためなんですが、 その便利さについて語るにはここではちょっと狭いのでまた別の記事に書きます(たぶん)。
↓個人的には、こういうIssueを立ててたりしたので待望の機能だったりします。
Local backends
dtplyr
data.table用のパッケージらしいです。私はdata.table使わないのでよく分かりません…
Tibble
tbl_df
用のパッケージらしいです。詳しくはtibble 1.0.0 | RStudio Blogを読めとのこと。
いくつかtbl_df
をいじるときに便利そうな関数が用意されています。
all_equal()
: すべての要素が同じかチェックする関数。lst()
: リストをつくる関数。data_frame()
と同じような感じ。add_row()
: 行を1つ追加する関数。↓のような感じで使える。
df <- data_frame(x = 1:3, y = 3:1) add_row(df, x = 4, y = 0) #> Source: local data frame [4 x 2] #> #> x y #> <dbl> <dbl> #> 1 1 3 #> 2 2 2 #> 3 3 1 #> 4 4 0
tbl_cube
2次元以上のデータをうまいこと扱うためのクラスらしいです。
Remote backends
SQLite
SQL translation
(この辺よく分からないので割愛します…)
Internals
SQLへの変換の仕方がけっこう変わってるっぽいんですが、元を知らないのでよく分かりませんでした。リリースノートにもわりと詳し目に書いてありますが、いずれ改めて解説が出てくる気がするので、それを待つことにします。
Minor improvements and bug fixes
空の値や欠損値などの取り扱いがいい感じに改良されていたりします。
感想
けっこう便利関数が増えている。いいことですね。ガンガン使っていきましょう。
lst()
とかtbl_cube()
とかいう関数ができてることから察するに、dplyrはdata.frameとは異なるようなデータも扱う方向に進もうとしているようにも見えます。
野心的すぎて事故らなければいいですが。。とはいえdata.frame以外も扱えると色々処理の幅が広がりそうなので期待したいです。
内部の話はもうちょいわかるようになりたいです。カラテが足りない…