Rユーザーの方でdplyrパッケージの便利さの虜になっている方は多いのではないでしょうか?
※私ものその一人ですw。このパッケージがなかったらRを使っていないかも・・・。
さて、そんな便利なdplyrパッケージですが、さらに便利に使うための方法としてdplyrパッケージで変数を使ったfor文の利用方法についてまとめたいと思います。
使い方の例としては、
- 大量にカラムがあるデータで、カラムごとに絞込み(filter)をしたい。
- カラムごとに異なる値を変数を使って絞込み(filter)をしたい。
- カラムごとにgroupbyして出力したい。 等々。
この記事でわかること。
- dplyrパッケージでfor文のコードの書き方がわかります。
- 変数を使って条件を可変にしながらdplyrの処理の仕方がわかります
- dplyrパッケージでfor文を使えると、大量のデータ処理が素早く少ないコードで記述ができます。
では、始めて行きましょう。
Contents
dplyrでカラム名や値を変数で指定してもうまく認識されない。
例えば↓を実行した場合、irisのデータにはhogeというカラムはありません。とエラーが出てSepal.Lengthのカラムを選択してくれません。
1 2 3 4 5 |
library(dplyr) hoge <- names(iris)[1] #hogeにはirisの中の1列目のカラムの名称(=Sepal.Length)を代入している iris %>% select(hoge) エラー: Can't subset columns that don't exist. x Column `hoge` doesn't exist. |
考えてみたら当たり前で、select(Species)と記述したら「Species」のカラムをとってきてくれますが、hogeというカラムはないわけで・・。irisの中のカラムの名前とhogeという変数の区別ができないからこういう状況になるわけです。
では、どうしたらうまくいくのでしょうか? 正解はこちら
1 2 3 4 5 6 7 8 9 10 11 |
library(dplyr) hoge <- names(iris)[1] iris %>% select(.data[[hoge]]) %>% head() Sepal.Length 1 5.1 2 4.9 3 4.7 4 4.6 5 5.0 6 5.4 |
先ほどと違うところはhogeが.data[[hoge]]という記述になっていますね。 .dataというのは処理中のデータフレームを表す代名詞です。今回のコードの例だとirisの代名詞の役割を果たしています。
つまり、.data[[hoge]]と書くことで、処理中のデータフレームのhogeという変数に格納されている(今回の場合はSepal.Length)をselectしてください。といった指示のコードを意味します。
同じような書き方ですがダブルクォーテーションで囲むと、文字列と同じ意味でプログラムが動作します。
1 |
iris %>% select(.data[["Sepal.Length"]]) |
また、最初の例(下の記述)のようにダブルクォーテーションがない状態だと、変数として認識されます。
1 2 |
hoge <- names(iris)[1] iris %>% select(.data[[hoge]]) |
詳しい解説はこちら(https://dplyr.tidyverse.org/articles/programming.html#data-masking-1)を参考にしてもらうことにして、実際のコードの書き方について以後説明をします。
カラム名を変数として扱う場合。(mutateでの新規カラム作成は除く)
カラム名に変数を使ってコードを書く場合(select(hoge),filter(hoge==test)、group_by(hoge)といった具合)を紹介します。ただし、mutate、transmuteの場合はこの方法ではうまくいかないので、後ほど説明します。
先ほど紹介した方法と同じですが、以下のような書き方で対処ができます。
・selectの場合
1 2 3 4 5 6 7 8 9 10 11 |
library(dplyr) hoge <- names(iris)[1] iris %>% select(.data[[hoge]]) %>% head() Sepal.Length 1 5.1 2 4.9 3 4.7 4 4.6 5 5.0 6 5.4 |
・group_byの場合
1 2 3 4 5 6 7 8 9 10 |
library(dplyr) hoge <- names(iris)[5]#カラム名Speciesをhogeに代入 iris %>% group_by(.data[[hoge]]) %>% summarise(n=n()) # A tibble: 3 x 2 Species n <fct> <int> 1 setosa 50 2 versicolor 50 3 virginica 50 |
・summariseの場合
1 2 3 4 5 6 7 8 9 |
library(dplyr) hoge <- names(iris)[1] iris %>% group_by(Species) %>% summarise(sum_ex = sum(.data[[hoge]])) Species sum_ex <fct> <dbl> 1 setosa 250. 2 versicolor 297. 3 virginica 329. |
・relocateの場合
1 2 3 4 5 6 7 8 9 10 11 |
library(dplyr) hoge <- names(iris)[5] #カラム名Speciesをhogeに代入 iris %>% relocate(.data[[hoge]], Petal.Width) %>% head() Species Petal.Width Sepal.Length Sepal.Width Petal.Length 1 setosa 0.2 5.1 3.5 1.4 2 setosa 0.2 4.9 3.0 1.4 3 setosa 0.2 4.7 3.2 1.3 4 setosa 0.2 4.6 3.1 1.5 5 setosa 0.2 5.0 3.6 1.4 6 setosa 0.4 5.4 3.9 1.7 |
他にもいろいろな活用シーンがあるかと思いますが、とりあえず記述の例はこんなところです。
普段カラム名をコードで書くところに.data[[hoge]]と書くことで、hogeという変数に格納されている内容のカラムに対して処理を行ってくれます。
mutateやtransmuteでのカラム作成する場合
mutateやtransmuteの場合は先ほどと同じ書き方では動きません。実際のコードサンプルはこちら。
1 2 3 4 5 6 7 8 9 10 11 |
library(dplyr) hoge2 <- paste0("test","_1") iris %>% mutate("{hoge2}":=Petal.Length+1) Sepal.Length Sepal.Width Petal.Length Petal.Width Species test_1 1 5.1 3.5 1.4 0.2 setosa 2.4 2 4.9 3.0 1.4 0.2 setosa 2.4 3 4.7 3.2 1.3 0.2 setosa 2.3 4 4.6 3.1 1.5 0.2 setosa 2.5 5 5.0 3.6 1.4 0.2 setosa 2.4 6 5.4 3.9 1.7 0.4 setosa 2.7 |
1 2 3 4 5 6 7 8 9 10 11 |
library(dplyr) hoge2 <- paste0("test","_1") iris %>% transmute("{hoge2}":=Petal.Length+2) %>% head() test_1 1 3.4 2 3.4 3 3.3 4 3.5 5 3.4 6 3.7 |
mutateなどでカラムを作成する場合は、「"{hoge2}":=」といった感じで変数をダブルクォーテーションと波括弧で囲みます。また、=ではなく:⁼という表記で表現します。とりあえずは、こういう書き方をすれば動く・・・と理解してもらえれば実用上はOKだと思います。
filterなど値を変数で指定する場合。
filterで値を指定する場合は、特に何も意識する必要はなく変数をそのまま指定すればOKです。
1 2 3 4 5 6 7 8 9 10 11 |
library(dplyr) hoge3 <- "setosa" iris %>% filter(Species==hoge3) %>% head() 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 4 4.6 3.1 1.5 0.2 setosa 5 5.0 3.6 1.4 0.2 setosa 6 5.4 3.9 1.7 0.4 setosa |
dplyrでfor文を使ったサンプル
それでは実際にtidyverseライブラリーに入っているmpgのデータを使ってfor文で繰り返し処理を行う例を紹介します。
あまり実用的ではありませんが、カラムを1つずつselectしてprintする例を書いてみます。
※コードの実行結果は大量に出力されるので省略します。
1 2 3 4 5 6 |
library(tidyverse) hogehoge <- names(mpg) for (i in hogehoge){ mpg %>% select(.data[[i]]) %>% print() } |
コードの解説
- hogehogeにmpgのカラム名のベクトルを代入します。
- for文でhogehogeの名前ベクトルの1番目から順番に変数iに格納します。
- 格納された変数iを.data代名詞に代入し、select(変数iに格納されているカラムがselectされる)してprintします。
続いてもう少し実用的な例として、character型のカラムでgroup_byして集計してみます。イメージとしては、次のカラム名「manufacturer model、trans、drv、fl、class」のカラムごとにgroup_byして件数を確認する感じです。
※manufacturerでgroup_byして件数を数える、transでgroup_byして件数を数える。drvでgroup_byして・・と言った感じです。
1 2 3 4 5 6 |
library(tidyverse) hogehoge <- mpg %>% select(where(is.character)) %>% names() for (i in hogehoge){ mpg %>% group_by(.data[[i]]) %>% summarise(n=n()) %>% print() } |
コードの解説
- mpg %>% select(where(is.character)) %>% names()では、mpgのデータフレームからcharacterのカラムだけを選択、名前ベクトルをhogehogeに格納しています。
- for文でhogehogeの名前ベクトルの1番目から順番に変数iに格納します。
- 格納された変数iを.data代名詞に代入し、group_by&summarise(変数iに格納されているカラムでgroup_by&summariseされる)をしてprintします。
今度はmutateを利用した例を書いてみます。今度はintegerのカラムに+1したカラムを追加します。その際のカラム名は元のカラムの末尾に”_hoge”を追加します。
※yearのカラムに+1したカラムを作成し、そのカラム名を「year_hoge」とします。同様にcylにも+1してcyl_hoge、cty、hwyにも同様に処理を繰り返します。
1 2 3 4 5 6 7 |
library(tidyverse) hogehoge <- mpg %>% select(where(is.integer)) %>% names() for (i in hogehoge){ wk <- paste0(i,"_hoge") mpg <- mpg %>% mutate("{wk}":= .data[[i]] + 1) %>% print() } |
コードの解説
- mpg %>% select(where(is. integer)) %>% names()では、mpgのデータフレームからintegerのカラムだけを選択、名前ベクトルをhogehogeに格納しています。
- for文でhogehogeの名前ベクトルの1番目から順番に変数iに格納します。
- wk <- paste0(i,"_hoge")では、格納された変数iに_hogeを結合しています。(year_hoge,cyo_hoge・・と言った感じでカラム名を作成)
- wkで作成した変数とmutate関数を使って新たなカラムを作成します。(mutate("{wk}":= の部分がそれにあたります。)新たなカラムには元のカラムに+1したいので、.data[[i]] +1 で元のカラムのデータに+1します。
最後にfilterで絞り込む値をfor文で変更しながら実施する例を紹介します。
modelのカラムには車種名が入っていますが、a4、a4 quattro、a6 quattro・・と言った感じで、それぞれの値をfilterで絞込みして表示させてみます。
1 2 3 4 5 6 |
library(tidyverse) hogehoge <- mpg %>% select(model) %>% distinct() %>% pull() for (i in hogehoge){ mpg %>% filter(model == i) %>% print() } |
コードの解説
- mpg %>% select(model) %>% distinct() %>% pull()では、mpgのデータフレームのmodelのカラムのデータをユニーク化しベクトルhogehogeに格納しています。
- for文でhogehogeのベクトルの1番目から順番に変数iに格納します。
- filter(model == i)の部分でmodelの値をa4、a4 quattro、a6 quattro・・と変更しながらデータ全体の絞り込みを行っています。
まとめ
dplyr関数でfor文を実行する場合、利用する関数によって変数の扱い方・コードの書き方が変わりますが、下の3つのポイントを押さえておけば、dplyrでfor文を使うときに困ることはほぼないかと思います。
メモ
- select、group_by、filter、summarise、relocateなどカラム名を指定する場合.data[[hoge]]と記述する。
- mutate、transmuteの場合は、"{hoge2}"とダブルクォーテーションと波括弧で囲い、=の代わりに:=とする。
- filterで指定する値を変数で利用する場合は、そのまま変数を指定する。
個人的にはデータの加工や前処理を行う場合など、かなり高頻度で使う書き方なので覚えておくとデータ加工の幅が広がってよいと思います。
※以前はquoとかenquoとかsymとか!!とかで書いていたのですが、最近はかなりシンプルに書けるようになってきたのでものすごく楽になりました・・。
それでは。