elsur.jpn.org >

« 読了:Asparouhov, Hamaker, & Muthen (2017) ものども跪け、これがMplus8の新機能「動的SEM」だ | メイン | 読了:Little & Wu (1991) 標本から得たAxBクロス表を既知の周辺分布に合わせたい、標本にはバイアスがあることがわかっている、さあどうするか »

2017年8月19日 (土)

 Rのdplyrパッケージを使っていて困ることのひとつに、非標準評価(NSE)をめぐるトラブルがある。dplyrの関数のなかでは変数名を裸で指定できることが多い。これはとても便利なんだけど、ときにはかえって困ることもある。以前はいろんな関数に非標準評価版と標準評価版が用意されていたんだけど、最近は方針が変わったようだ。
 これはきっと深い話なんだろうけど、きちんと調べている時間がない。かといって、いざ困ったときにあわてて調べているようでは追いつかない。しょうがないので、dplyrのvignettesのひとつ"Programming with dplyr"を通読してメモを取っておくことにする。Rの達人からみればつまらない情報だと思うが、すいません、純粋に自分用の覚え書きです。

●.data代名詞。たとえば...

mutate_y <- function(df){
mutate(df, y= .data$a + .data$b)
}
mutate_y(df1)

上の例ではa, bがdfの変数であることを明示している。仮に.dataをつけないと、dfの変数の中にa,bがないとき、グローバル環境にあるa,bが読まれちゃうかもしれない。
[←そうそう、そういうトラブルが時々ある。しょうがないから私は、危なそうなときはdplyrを呼ばず df$y <- df[,"a"] + df[,"b"] という風にクラシカルに書くか、関数の冒頭で stopifnot(c("a", "b") %in% colnames(df)) とトラップしていた。そうか.dataって書けばいいのか]

●quo()。たとえば、次のコードは通らない。

df <- tibble(
g1 = c(1,1,2,2,2),
a = sample(5)
)
my_summarize <- function(df, group_var){
df %>%
group_by(group_var) %>%
summarize(a = mean(a))
}
# my_summarize(df, g1) はだめ
# my_summarize(df, "g1") もだめ

そこで、関数にquosureを渡す。quo()は入力を評価せずクオートし、quosureと呼ばれるオブジェクトを返す。!!は入力をアンクオートする。UQ()と書いてもよい。

my_summarize <- function(df, group_var){
df %>%
group_by(!!group_var) %>%
summarize(a = mean(a))
}
my_summarize(df, quo(g1))

●enquo()。上の例で、関数に裸の変数名を渡したいとしよう。enquo()は謎の黒魔術を用い(ほんとにそう書いてある)、ユーザが関数に与えた引数そのもの(ここではg1という変数名)をクオートしてくれる。

my_summarize <- function(df, group_by){
group_by <- enquo(group_by)
df %>%
group_by(!!group_var) %>%
summarize(a = mean(a))
}
my_summarize(df, g1)

●quo_name()。たとえば

mutate(df, mean_a = mean(a), sum_a = sum(a))
mutate(df, mean_b = mean(b), sum_b = sum(b))

というようなのを関数にしたい。つまり、mean_a, sum_aといった新しい変数名を生成したいわけだ。こういうときはquo_name()でquosureを文字列に変換する。mutate()のなかの式の左辺で!!を使った時は、=ではなく:=を使う。

mu_mutate <- function(df, expr){
expr <- enquo(expr)
mean_name <- paste0("mean_", quo_name(expr))
mutate(df, !!mean_name := mean(!!expr)
}
my_mutate(df, a)

●enquos()。下の例では複数の裸の変数名を渡している。enquos()は...をquosureのリストにして返す。!!!はquosureのリストをアンクオートしてつないでくれる(これをunquote-splicingという)。UQS()と書いても良い。

my_summarize <- function(df, ...){
group_var <- enquos(...)
df %>%
group_by(!!!group_var ) %>%
summarize(a = mean(a))
}
my_summarize(df, g1, g2)

 こうして書いてみると一見ややこしいけど、SASマクロ言語のクオテーションと比べれば全然わかりやすい。ああ、懐かしい...あれはほんとに、ほんとにわけがわからなかった...

 ところで、vignetteにいわく、疑似クオテーション(quasiquotation)という概念は哲学者W.V.O.クワインの40年代の著述に由来するのだそうだ。へえええ?

2020/01/22 ちょっぴり追記。

雑記:データ解析 - Rのdplyrパッケージでプログラミングするときの注意点