タイトルのとおりです。気分転換を兼ねてメモ。

library(dplyr)
#>  
#>   次のパッケージを付け加えます: 'dplyr'
#>   以下のオブジェクトは 'package:stats' からマスクされています: 
#>  
#>       filter, lag
#>   以下のオブジェクトは 'package:base' からマスクされています: 
#>  
#>       intersect, setdiff, setequal, union

列位置を示す数値ベクトルを与えると,それ引っ張ってくる:

select(iris, c(1, 4)) %>% head(3)
#>    Sepal.Length Petal.Width
#>  1          5.1         0.2
#>  2          4.9         0.2
#>  3          4.7         0.2

starts_with()などのselect_helpers関数群は,要するに列位置を表す数値ベクトルを返してくる:

starts_with("s", vars = names(iris))
#>  [1] 1 2 5
ends_with("s", vars = names(iris))
#>  [1] 5
contains("se", vars = names(iris))
#>  [1] 1 2
matches("^.e", vars = names(iris))
#>  [1] 1 2 3 4
one_of(c("Sepal.Length", "Species"), vars = names(iris))
#>  [1] 1 5
everything(vars = names(iris))
#>  [1] 1 2 3 4 5

ちなみに引数のvarsはデフォルトではvars = current_vars()となっており,select()などの中で使うと自動的に.data引数に指定してあるデータセットから変数名リストを取得してくるというもの。なのでselect()内で指定しなくてもいい感じにしてくれる。

変数をselectする際,名前付きで指定すると変数名を変更してくれてピックアップしてくる:

select(iris, kosaki = 5, Sepal.Length) %>% head(3)
#>    kosaki Sepal.Length
#>  1 setosa          5.1
#>  2 setosa          4.9
#>  3 setosa          4.7

これが地味に強力で,以下のようなことも可能:

select(iris, kosaki = 1:3) %>% head(3)
#>    kosaki1 kosaki2 kosaki3
#>  1     5.1     3.5     1.4
#>  2     4.9     3.0     1.4
#>  3     4.7     3.2     1.3
select(iris, kosaki = starts_with("s")) %>% head(3)
#>    kosaki1 kosaki2 kosaki3
#>  1     5.1     3.5  setosa
#>  2     4.9     3.0  setosa
#>  3     4.7     3.2  setosa

数値ベクトルで指定できるんだから,こういうことだって可能:

select(iris, length(current_vars()):3) %>% head(3)
#>    Species Petal.Width Petal.Length
#>  1  setosa         0.2          1.4
#>  2  setosa         0.2          1.4
#>  3  setosa         0.2          1.3

current_vars()を使う理由は,pipeプロセス中で,この時点での変数リストを持ってきたいから。この方が応用できるんで。

変数の並びを入れ替えたい場合,お約束としてはeverything()を活用する:

select(iris, 1, 5, everything()) %>% head(3)
#>    Sepal.Length Species Sepal.Width Petal.Length Petal.Width
#>  1          5.1  setosa         3.5          1.4         0.2
#>  2          4.9  setosa         3.0          1.4         0.2
#>  3          4.7  setosa         3.2          1.3         0.2

あとはorderとかも使える:

select(iris, order(current_vars())) %>% head(3)
#>    Petal.Length Petal.Width Sepal.Length Sepal.Width Species
#>  1          1.4         0.2          5.1         3.5  setosa
#>  2          1.4         0.2          4.9         3.0  setosa
#>  3          1.3         0.2          4.7         3.2  setosa

select()だと,多分こんな感じ。

rename()は関数名につられて「変数名を変更する関数」と思われがちだけど,select()との比較で考えたほうが適切。

select()はヒットした変数を取り出す(もしくは名前を変更)して,それ以外を除外する。一方rename()は基本select()と同じ動きをして,ヒットしなかった変数を残す(後ろに回す)。内部的にはselect()で変数ピックアップするときに,特に指定していない場合は変数名と同じ文字列を名前として付与して,それを適用してるみたい。rename()はこの後に変数をドロップしないように残ったものを拾っているみたいで,そこが違いみたい。

select_all()を使ってみる。HelpのExampleより:

select_all(mtcars, toupper) %>% head(3)
#>                 MPG CYL DISP  HP DRAT    WT  QSEC VS AM GEAR CARB
#>  Mazda RX4     21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
#>  Mazda RX4 Wag 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
#>  Datsun 710    22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
select_all(mtcars, "toupper") %>% head(3)
#>                 MPG CYL DISP  HP DRAT    WT  QSEC VS AM GEAR CARB
#>  Mazda RX4     21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
#>  Mazda RX4 Wag 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
#>  Datsun 710    22.8   4  108  93 3.85 2.320 18.61  1  1    4    1
select_all(mtcars, funs(toupper(.))) %>% head(3)
#>                 MPG CYL DISP  HP DRAT    WT  QSEC VS AM GEAR CARB
#>  Mazda RX4     21.0   6  160 110 3.90 2.620 16.46  0  1    4    4
#>  Mazda RX4 Wag 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4
#>  Datsun 710    22.8   4  108  93 3.85 2.320 18.61  1  1    4    1

全ての変数名に対して関数が適用される,という感じ。それはいいんだけど,rename_all()も存在する。この2つ,微妙にコードは違うんだけど,使い分けることってあるのかな…。浮かばない。

次にselect_at()を使ってみる。これは,第二引数である.varsに残す変数を指定して,.fun引数で変数名を処理する関数を指定するという動き。

select_at(iris, c("Sepal.Length", "Petal.Length"), .funs = toupper) %>% head(3)
#>    SEPAL.LENGTH PETAL.LENGTH
#>  1          5.1          1.4
#>  2          4.9          1.4
#>  3          4.7          1.3
select_at(iris, vars(starts_with("s")), .funs = toupper) %>% head(3)
#>    SEPAL.LENGTH SEPAL.WIDTH SPECIES
#>  1          5.1         3.5  setosa
#>  2          4.9         3.0  setosa
#>  3          4.7         3.2  setosa

.varには,変数名の文字列ベクトルならそのまま,列番号ならvars()で挟み込む必要があるみたい。なお.funs引数を省略すると現在の変数名をそのまま渡すので変数名は変わらず:

select_at(iris, vars(starts_with("s"))) %>% head(3)
#>    Sepal.Length Sepal.Width Species
#>  1          5.1         3.5  setosa
#>  2          4.9         3.0  setosa
#>  3          4.7         3.2  setosa

使い方としてはこっちがメイン,かな? なおrename_at()関数もある:

rename_at(iris, vars(starts_with("s")), .funs = toupper) %>% head(3)
#>    SEPAL.LENGTH SEPAL.WIDTH Petal.Length Petal.Width SPECIES
#>  1          5.1         3.5          1.4         0.2  setosa
#>  2          4.9         3.0          1.4         0.2  setosa
#>  3          4.7         3.2          1.3         0.2  setosa

.var引数で選択した変数に対して.funs引数で指定した変数名変更用関数を適用し,残りはそのまま持ってくる,という仕事をする。こっちは変数名を変更することが前提なので,.funsを指定しないとエラーで怒られる。これまでの挙動を考えると納得いかない…。

最後にselect_if()を使ってみる。これは,第二引数である.predicateに関数を準備し,各変数をこの関数に当てていく。そしてTRUEを返した関数を残す(選択する),という仕事をする。ベタな例を以下に:

select_if(iris, is.numeric) %>% head(3)
#>    Sepal.Length Sepal.Width Petal.Length Petal.Width
#>  1          5.1         3.5          1.4         0.2
#>  2          4.9         3.0          1.4         0.2
#>  3          4.7         3.2          1.3         0.2

各変数にis.numericを適用し,TRUE(つまり数値の変数)だけを取り出してる。なお,関数を自作して適用するのも可能:

f <- function(x) {
 if(is.numeric(x)){
   mean(x) > 3
 } else {
   return(TRUE)
 }
}
select_if(iris, f) %>% head(3)
#>    Sepal.Length Sepal.Width Petal.Length Species
#>  1          5.1         3.5          1.4  setosa
#>  2          4.9         3.0          1.4  setosa
#>  3          4.7         3.2          1.3  setosa

この場合,「変数が数値型の場合,平均が3より大きかったら残す。それ以外の型なら残す」という仕事をさせてる。また,列数と同じ数の論理値ベクトルを与えてもOK:

select_if(iris, c(TRUE, TRUE, FALSE, TRUE, FALSE)) %>% head(3)
#>    Sepal.Length Sepal.Width Petal.Width
#>  1          5.1         3.5         0.2
#>  2          4.9         3.0         0.2
#>  3          4.7         3.2         0.2

また,.Predicateで指定するのは関数だけでなくformulaでもいいみたい。省略。あと第三引数.funsはこれまでのパターン同様変数名を変更するための関数です。select_if()においてはオプショナルなものです:

select_if(iris, is.numeric, toupper) %>% head(3)
#>    SEPAL.LENGTH SEPAL.WIDTH PETAL.LENGTH PETAL.WIDTH
#>  1          5.1         3.5          1.4         0.2
#>  2          4.9         3.0          1.4         0.2
#>  3          4.7         3.2          1.3         0.2

一方,rename_if()select_if()とほぼ同じような動きをしますが,名前変更なので.funsが必須なのと,選択されなかった変数も残るという違いがあります:

rename_if(iris, is.numeric, toupper) %>% head(3)
#>    SEPAL.LENGTH SEPAL.WIDTH PETAL.LENGTH PETAL.WIDTH Species
#>  1          5.1         3.5          1.4         0.2  setosa
#>  2          4.9         3.0          1.4         0.2  setosa
#>  3          4.7         3.2          1.3         0.2  setosa

だいたいこんな感じです。

Enjoy!

sessionInfo()
#>  R version 3.4.2 (2017-09-28)
#>  Platform: x86_64-pc-linux-gnu (64-bit)
#>  Running under: Ubuntu 16.04.3 LTS
#>  
#>  Matrix products: default
#>  BLAS: /usr/lib/libblas/libblas.so.3.6.0
#>  LAPACK: /usr/lib/lapack/liblapack.so.3.6.0
#>  
#>  locale:
#>   [1] LC_CTYPE=ja_JP.UTF-8       LC_NUMERIC=C              
#>   [3] LC_TIME=ja_JP.UTF-8        LC_COLLATE=ja_JP.UTF-8    
#>   [5] LC_MONETARY=ja_JP.UTF-8    LC_MESSAGES=ja_JP.UTF-8   
#>   [7] LC_PAPER=ja_JP.UTF-8       LC_NAME=C                 
#>   [9] LC_ADDRESS=C               LC_TELEPHONE=C            
#>  [11] LC_MEASUREMENT=ja_JP.UTF-8 LC_IDENTIFICATION=C       
#>  
#>  attached base packages:
#>  [1] methods   stats     graphics  grDevices utils     datasets  base     
#>  
#>  other attached packages:
#>  [1] dplyr_0.7.4
#>  
#>  loaded via a namespace (and not attached):
#>   [1] Rcpp_0.12.13         bookdown_0.5         assertthat_0.2.0    
#>   [4] digest_0.6.12        rprojroot_1.2        R6_2.2.2            
#>   [7] backports_1.1.1      magrittr_1.5         evaluate_0.10.1     
#>  [10] blogdown_0.1         rlang_0.1.2          stringi_1.1.5       
#>  [13] bindrcpp_0.2         rmarkdown_1.6.0.9004 tools_3.4.2         
#>  [16] stringr_1.2.0        glue_1.1.1           yaml_2.1.14         
#>  [19] compiler_3.4.2       pkgconfig_2.0.1      htmltools_0.3.6     
#>  [22] bindr_0.1            knitr_1.17           tibble_1.3.4