LoginSignup
8
6

More than 3 years have passed since last update.

文字列で列名を指定してmutate & aesを文字列で指定して繰り返しggplot

Last updated at Posted at 2020-04-26

両方できる。そうrlangならね

rlangを調べているうちにこれらを行う方法を見つけたのでメモ。
困っている人がいたら役に立つかも?

今回は主に以下のページを参考にさせていただきました:

rlangについてはまだ勉強中で「動くけど原理をうまく説明できない」ため説明がほぼないです。ごめんなさい。めちゃくちゃ便利だけど説明が難しい。。。

仕組みが気になる方は上記ページや「R 非標準評価」で引っかかるページ等を参照していただければと思います。

コードを試す際にはtidyverseが必要なのでlibrary(tidyverse)をしてください。

文字列で列名を指定してmutate

例えば、「あるデータフレームに対して、数値が入った列の値を正規化し、因子が入った列の値を文字列にする」という作業をfor文で行いたいとします(実際はmutate_ifでやる方が簡単です)。
何も考えずにコードを書くと以下のようになります。colnamesで取得した列名(文字列)ごとに処理を行う心づもりです。

うまくいかない.R
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"))とはできないことが原因です。

解決策

以下のようにコードを書き換えるとうまくいきます。

うまくいく.R
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で取得した列名(文字列)ごとに処理を行う心づもりです。

うまくいかない.R
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を使ったひと工夫が必要です。

以下のようにコードを書き換えるとうまくいきます。

うまくいく.R
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を組み込んだ関数を作ろうとするときなどに非常に重要になるそうです。

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6