【R言語】条件を満たす行の複数列に代入してくれるmutate_cond
下記のようなデータがあります。という関係です。
data<-data.frame(letter=c("a","a","c","b","a"), x1=c(20,30,15,25,20), x2=c(5,4,10,4,3)) %>% mutate(y=x1*x2) data
## letter x1 x2 y
## 1 a 20 5 100
## 2 a 30 4 120
## 3 c 15 10 150
## 4 b 25 4 100
## 5 a 20 3 60
このデータに対して、
letterが“a”である行の、x1に5を足してx2には10を足し、yを計算しなおす。という操作を加えるとき、どういう操作が思いつくでしょうか。
%<>%
を使った処理
magrittr
パッケージのパイプライン演算子%<>%
を使った処理がおそらく一般的だと思います。
x %<>% f()
は、x <- x %>%
f()
と置き換えられるパイプライン演算子で、データフレームの置き換えを効率化してくれます(雑)。
data[data$letter=="a",] %<>% mutate(x1=x1+5,x2=x2+10,y=x1*x2) data
## letter x1 x2 y
## 1 a 25 15 375
## 2 a 35 14 490
## 3 c 15 10 150
## 4 b 25 4 100
## 5 a 25 13 325
しかしこの方法は行の抽出の仕方がスマートではありません。条件が複数あるような時にグチャグチャになってしまいます。
さらに、dplyrチェーンの中で代入したいな~なんて時にも困ります。
dplyrチェーンで繋げる
そこで考えられるのがもういっそ気合でdplyrチェーンで済ませてしまおうというやり方です。
data %>% mutate(x1=if_else(letter=="a" , x1+5 , x1), x2=if_else(letter=="a" , x2+10 , x2)) %>% mutate(y=x1*x2)
## letter x1 x2 y
## 1 a 25 15 375
## 2 a 35 14 490
## 3 c 15 10 150
## 4 b 25 4 100
## 5 a 25 13 325
この処理だとパイプライン処理を崩さず代入できますが、
複数列置き換える場合、その数だけif_else()
を書く必要があり若干煩雑です。
mutate_cond
filter()
とmutate()
が合体したような関数があればいいんだけどな~などと考えているときに見つけたのがこの記事。
https://stackoverflow.com/questions/34096162/dplyr-mutate-replace-several-columns-on-a-subset-of-rows
どうやらmutate_cond()
という関数を定義して使っているらしい。
Create a simple function for data frames or data tables that can be incorporated into pipelines. This function is like mutate but only acts on the rows satisfying the condition
パイプライン中に挿入でき、条件を満たす行にしか作用しないmutate()
みたいなヤツらしい。
思わず「こういうのでいいんだよ」と言ってしまいそうなコンセプトですね、早速使ってみましょう。第一引数がデータ、第二が条件、第三以降はmutate()
と同じです。
mutate_cond <- function(.data, condition, ..., envir = parent.frame()) { condition <- eval(substitute(condition), .data, envir) .data[condition, ] <- .data[condition, ] %>% mutate(...) .data } data %>% mutate_cond(letter=="a" , x1=x1+5 , x2=x2+10 ,y=x1*x2)
## letter x1 x2 y
## 1 a 25 15 375
## 2 a 35 14 490
## 3 c 15 10 150
## 4 b 25 4 100
## 5 a 25 13 325
素晴らしい。引数の指定も簡単で、本家にあってもおかしくないようなdplyr
ライクな関数です。
これでみなさんも捗っていきましょう。