困惑しそうな変更点の話は書いたので、次はテンション上がりそうな新機能の話を書きたいと思います。
group_nest()
、group_split()
group_nest()
とgroup_split()
はいずれもグループごとにデータフレームを分割する関数です。
違いは、group_nest()
だとnestされたデータフレームの列をつくります(tidyr::nest()
と同じ)。
group_split()
はデータフレームのリストを返します(split()
と同じ)。
library(dplyr, warn.conflicts = FALSE) mtcars %>% group_by(cyl) %>% group_nest() #> # A tibble: 3 x 2 #> cyl data #> <dbl> <list> #> 1 4 <tibble [11 x 10]> #> 2 6 <tibble [7 x 10]> #> 3 8 <tibble [14 x 10]> mtcars %>% group_by(cyl) %>% group_split() #> [[1]] #> # A tibble: 11 x 11 #> mpg cyl disp hp drat wt qsec vs am gear carb #> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> #> 1 22.8 4 108 93 3.85 2.32 18.6 1 1 4 1 #> 2 24.4 4 147. 62 3.69 3.19 20 1 0 4 2 #> 3 22.8 4 141. 95 3.92 3.15 22.9 1 0 4 2 #> 4 32.4 4 78.7 66 4.08 2.2 19.5 1 1 4 1 #> 5 30.4 4 75.7 52 4.93 1.62 18.5 1 1 4 2 #> 6 33.9 4 71.1 65 4.22 1.84 19.9 1 1 4 1 #> 7 21.5 4 120. 97 3.7 2.46 20.0 1 0 3 1 #> 8 27.3 4 79 66 4.08 1.94 18.9 1 1 4 1 #> 9 26 4 120. 91 4.43 2.14 16.7 0 1 5 2 #> 10 30.4 4 95.1 113 3.77 1.51 16.9 1 1 5 2 #> 11 21.4 4 121 109 4.11 2.78 18.6 1 1 4 2 #> #> [[2]] #> # A tibble: 7 x 11 #> mpg cyl disp hp drat wt qsec vs am gear carb #> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> #> 1 21 6 160 110 3.9 2.62 16.5 0 1 4 4 #> 2 21 6 160 110 3.9 2.88 17.0 0 1 4 4 #> 3 21.4 6 258 110 3.08 3.22 19.4 1 0 3 1 #> 4 18.1 6 225 105 2.76 3.46 20.2 1 0 3 1 #> 5 19.2 6 168. 123 3.92 3.44 18.3 1 0 4 4 #> 6 17.8 6 168. 123 3.92 3.44 18.9 1 0 4 4 #> 7 19.7 6 145 175 3.62 2.77 15.5 0 1 5 6 #> #> [[3]] #> # A tibble: 14 x 11 #> mpg cyl disp hp drat wt qsec vs am gear carb #> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> #> 1 18.7 8 360 175 3.15 3.44 17.0 0 0 3 2 #> 2 14.3 8 360 245 3.21 3.57 15.8 0 0 3 4 #> 3 16.4 8 276. 180 3.07 4.07 17.4 0 0 3 3 #> 4 17.3 8 276. 180 3.07 3.73 17.6 0 0 3 3 #> 5 15.2 8 276. 180 3.07 3.78 18 0 0 3 3 #> 6 10.4 8 472 205 2.93 5.25 18.0 0 0 3 4 #> 7 10.4 8 460 215 3 5.42 17.8 0 0 3 4 #> 8 14.7 8 440 230 3.23 5.34 17.4 0 0 3 4 #> 9 15.5 8 318 150 2.76 3.52 16.9 0 0 3 2 #> 10 15.2 8 304 150 3.15 3.44 17.3 0 0 3 2 #> 11 13.3 8 350 245 3.73 3.84 15.4 0 0 3 4 #> 12 19.2 8 400 175 3.08 3.84 17.0 0 0 3 2 #> 13 15.8 8 351 264 4.22 3.17 14.5 0 1 5 4 #> 14 15 8 301 335 3.54 3.57 14.6 0 1 5 8
group_nest()
とgroup_split()
は、以下のようにgroup_by()
をかまさずに直接グループ化に使う列を指定することもできます。
mtcars %>% group_nest(cyl) mtcars %>% group_split(cyl)
group_map()
do()
がdeprecatedになって幾星霜、ついに決定版が来ました。
グループ化に使っている列以外の列をグループごとに分けたdata.frame
に関数を適用することができます。
library(dplyr, warn.conflicts = FALSE) mtcars %>% group_by(cyl) %>% group_map(~ broom::tidy(lm(mpg ~ disp, data = .x))) #> # A tibble: 6 x 6 #> # Groups: cyl [3] #> cyl term estimate std.error statistic p.value #> * <dbl> <chr> <dbl> <dbl> <dbl> <dbl> #> 1 4 (Intercept) 40.9 3.59 11.4 0.00000120 #> 2 4 disp -0.135 0.0332 -4.07 0.00278 #> 3 6 (Intercept) 19.1 2.91 6.55 0.00124 #> 4 6 disp 0.00361 0.0156 0.232 0.826 #> 5 8 (Intercept) 22.0 3.35 6.59 0.0000259 #> 6 8 disp -0.0196 0.00932 -2.11 0.0568
これは以下のようにnest()
してmap()
してunnest()
して、というパターンとだいたい同じです(グループ化が残る点が違う)。
mtcars %>% group_by(cyl) %>% tidyr::nest() %>% mutate(data = purrr::map(data, ~ broom::tidy(lm(mpg ~ disp, data = .)))) %>% tidyr::unnest()
ちなみに、こっちはgroup_nest()
、group_split()
と違ってグループ化の変数を直接指定することはできません。
nest_join()
nest_join()
については昔にメモったgistと変わってなさそうなので再掲(unnest()
の挙動は相変わらず謎...)。
group_data()
、group_rows()
、group_keys()
group_data()
はすでに紹介しましたが、group_rows()
とgroup_keys()
もグループの情報にアクセスするための関数です。
group_data()
は全部入り、group_rows()
は各グループの行のインデックス、group_keys()
は各グループのキーの情報が取れます。
必要に応じて使い分けましょう。
g <- mtcars %>% group_by(cyl) group_data(g) #> # A tibble: 3 x 2 #> cyl .rows #> <dbl> <list> #> 1 4 <int [11]> #> 2 6 <int [7]> #> 3 8 <int [14]> group_rows(g) #> [[1]] #> [1] 3 8 9 18 19 20 21 26 27 28 32 #> #> [[2]] #> [1] 1 2 4 6 10 11 30 #> #> [[3]] #> [1] 5 7 12 13 14 15 16 17 22 23 24 25 29 31 group_keys(g) #> # A tibble: 3 x 1 #> cyl #> <dbl> #> 1 4 #> 2 6 #> 3 8
group_cols()
これはgrouped_df
に対してグループ化に使っている列を表すヘルパ関数です。select()
とか_at()
の関数の中で使えます。
具体的にどういうときに使うかというと、公式記事に紹介されてる例がわかりやすいです。
mutate_at()
とかでグループ化に使っている列まで対象になってしまったとき、その列に変更を加えることはできないのでエラーになってしまいます。
g <- mtcars %>% group_by(cyl) g %>% mutate_at( vars(starts_with("c")), ~ . - mean(.) ) #> Error: Column `cyl` can't be modified because it's a grouping variable
そこで、-group_cols()
を加えて明示的に対象から外してやるという手が使えます。
(そこまでわかってるなら勝手に外してほしい...とは思いつつ)
g %>% mutate_at( vars(starts_with("c"), -group_cols()), ~ . - mean(.) ) #> # A tibble: 32 x 11 #> # Groups: cyl [3] #> mpg cyl disp hp drat wt qsec vs am gear carb #> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> #> 1 21 6 160 110 3.9 2.62 16.5 0 1 4 0.571 #> 2 21 6 160 110 3.9 2.88 17.0 0 1 4 0.571 #> 3 22.8 4 108 93 3.85 2.32 18.6 1 1 4 -0.545 #> 4 21.4 6 258 110 3.08 3.22 19.4 1 0 3 -2.43 #> 5 18.7 8 360 175 3.15 3.44 17.0 0 0 3 -1.5 #> 6 18.1 6 225 105 2.76 3.46 20.2 1 0 3 -2.43 #> 7 14.3 8 360 245 3.21 3.57 15.8 0 0 3 0.5 #> 8 24.4 4 147. 62 3.69 3.19 20 1 0 4 0.455 #> 9 22.8 4 141. 95 3.92 3.15 22.9 1 0 4 0.455 #> 10 19.2 6 168. 123 3.92 3.44 18.3 1 0 4 0.571 #> # ... with 22 more rows
last_col()
これもselect()
のヘルパ関数のひとつです。一番最後の列を指すので、↓こんな感じで、
「n番目の列から最後の列までを選択」みたいな範囲指定で便利です。
as_tibble(mtcars) %>% select(3:last_col()) #> # A tibble: 32 x 9 #> disp hp drat wt qsec vs am gear carb #> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> #> 1 160 110 3.9 2.62 16.5 0 1 4 4 #> 2 160 110 3.9 2.88 17.0 0 1 4 4 #> 3 108 93 3.85 2.32 18.6 1 1 4 1 #> 4 258 110 3.08 3.22 19.4 1 0 3 1 #> 5 360 175 3.15 3.44 17.0 0 0 3 2 #> 6 225 105 2.76 3.46 20.2 1 0 3 1 #> 7 360 245 3.21 3.57 15.8 0 0 3 4 #> 8 147. 62 3.69 3.19 20 1 0 4 2 #> 9 141. 95 3.92 3.15 22.9 1 0 4 2 #> 10 168. 123 3.92 3.44 18.3 1 0 4 4 #> # ... with 22 more rows
funs()
の代わりにpurrrパッケージと同じく~
で関数をつくるように
さっきのmutate_at()
の例でサラッと流しましたが、この~ . - mean(.)
は以前であれば
funs(. - mean(.))
とか書いていたはずです。実はfuns()
は非推奨になって、purrrパッケージと同じく~
で関数をつくるようになりました。
g %>% mutate_at( vars(starts_with("c"), -group_cols()), ~ . - mean(.) )
複数の関数を指定したい場合はlist()
を使います。
data.frame(x = 1:2, y = 3:4) %>% mutate_all(list(plus1 = ~ . + 1, plus10 = ~ . + 10)) #> x y x_plus1 y_plus1 x_plus10 y_plus10 #> 1 1 3 2 4 11 13 #> 2 2 4 3 5 12 14
感想
あと、dplyr的に大きいのはHybrid evaluationの仕組みが変わったことなんですが、 ユーザーからするとあんまり意識することはなさそうなので省きました。興味ある人はこのvignetteとかを読んでください(私は読んでません)。
group_map()
とかはまだexperimentalなので長く使うコードに取り入れるのは不安ですけど、便利っぽいなので使っていきましょう。