両方できる。そうrlangならね
rlangを調べているうちにこれらを行う方法を見つけたのでメモ。
困っている人がいたら役に立つかも?
今回は主に以下のページを参考にさせていただきました:
- Tidy evaluation with rlang : : CHEAT SHEET
- 雰囲気で理解するtidy evaluation(1): tidy evaluationの導入
- rlang Reference
rlangについてはまだ勉強中で「動くけど原理をうまく説明できない」ため説明がほぼないです。ごめんなさい。めちゃくちゃ便利だけど説明が難しい。。。
仕組みが気になる方は上記ページや「R 非標準評価」で引っかかるページ等を参照していただければと思います。
コードを試す際にはtidyverseが必要なのでlibrary(tidyverse)
をしてください。
文字列で列名を指定してmutate
例えば、「あるデータフレームに対して、数値が入った列の値を正規化し、因子が入った列の値を文字列にする」という作業をfor文で行いたいとします(実際はmutate_if
でやる方が簡単です)。
何も考えずにコードを書くと以下のようになります。colnames
で取得した列名(文字列)ごとに処理を行う心づもりです。
test <- iris
for (col in colnames(test)){
target <- test %>% pull(col)
if (is.numeric(target)){
test <- test %>% mutate(col = scale(col))
} else if (is.factor(target)){
test <- test %>% mutate(col = as.character(col))
}
}
しかしこのコードは思うように動きません。
これはmutate
に対しては列名を引用符なしの状態で与える必要があることに起因します。つまり、mutate(iris, Species = as.character(Species))
とはできても、mutate(iris, "Species" = as.character("Species"))
とはできないことが原因です。
解決策
以下のようにコードを書き換えるとうまくいきます。
test <- iris
for (col in colnames(test)){
target <- test %>% pull(col)
colsym <- rlang::sym(col)
if (is.numeric(target)){
test <- test %>% mutate(!!col := scale(!!colsym))
} else if (is.factor(target)){
test <- test %>% mutate(!!col := as.character(!!colsym))
}
}
ポイントはmutate
内で
①左辺に!!
をつけて=
を:=
に変える
②右辺の列名を指定する変数はrlang::sym
で処理した後、!!
をつけて指定する
ことです。
こうすることによって変数を引用符のない列名として解釈させることができるようになります。
aesを文字列で指定して繰り返しggplot
例えば、「あるデータフレームをもとに、x軸はそのままでy軸を変えながら複数の図をggplotする」という作業をfor文で行いたいとします。
何も考えずにコードを書くと以下のようになります。こちらでもcolnames
で取得した列名(文字列)ごとに処理を行う心づもりです。
for (y in colnames(iris)) {
g <- ggplot(data = iris) + geom_boxplot(aes(x = Species, y = y))
x11(); plot(g)
}
しかしmutateの場合と同様うまくいきません。これはaes(x = Species, y = Sepal.Length)
をaes(x = Species, y = "Sepal.Length")
と書くことができないことに起因します。
解決例
aes内に文字列を使いたいだけであれば、文字列として変数に格納されている列名をggplotのaes()に入れたいという記事の解決法が参考になります。
しかし列名を変数で指定してfor文で実行しようとするとrlangを使ったひと工夫が必要です。
以下のようにコードを書き換えるとうまくいきます。
for (y in colnames(iris)) {
ys <- rlang::sym(y)
g <- ggplot(data = iris) + geom_boxplot(aes(x = Species, y = !!ys))
x11(); plot(g)
}
ポイントは列名が格納された変数をrlang::sym
で処理した後、aes
内で!!
をつけて指定することです。。mutateの②と同じです。
こうすることによって変数を引用符のない列名として解釈させることができます。
これらの問題の根底にあるのは、「関数が実行されるときRが引数をどのように解釈するのか」という普段あまり意識しない問題です。
この問題はdplyrを組み込んだ関数を作ろうとするときなどに非常に重要になるそうです。