Posted at

NSEとは何か

More than 5 years have passed since last update.


NSEとは何か

@dichikaさんによる素晴らしdplyrの紹介。

http://d.hatena.ne.jp/dichika/20141027

Non Standard Evaluation (NSE)とは、関数内部から、その関数を呼び出した時の引数のじゃなくて表現式そのものを、

関数の中での処理に利用しようという引数評価の方法です。

ある関数が引数を扱うとき、普通は興味あるのはその引数の

ところがRでは関数内部で、関数に与えられた引数の表現式を知ることができる。別にRに限った話じゃないけど。

以下のf()(通常評価)とg()(NSE)の違いを見れば一目瞭然で、

f = function(i, j, k) {

print(i)
print(j)
print(k)
}

g = function(i, j, k) {
print(substitute(i))
print(substitute(j))
print(substitute(k))
}

x = "orz"

f(i = 1, j = mean, k = x)
## [1] 1
## function (x, ...)
## UseMethod("mean")
## <bytecode: 0x7ff59bbb6ce0>
## <environment: namespace:base>
## [1] "orz"
g(i = 1, j = mean, k = x)
## [1] 1
## mean
## x

この引数に対するg()的な評価のしかたをNon Standard Evaluation (NSE)と呼ぶのである。

どう使うかというと、一般的にはデータフレームとかリストとかのデータを与えて、その要素を指定する状況が多分、圧倒的におおい。

例えば、

# Standard evaluation

m = function(d, i) {
d[[i]]
}

m(mtcars, "vs")
## [1] 0 0 1 1 0 1 0 1 1 1 1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 1 0 1 0 0 0 1
m(mtcars, vs)
## Error in (function(x, i, exact) if (is.matrix(i)) as.matrix(x)[[i]] else .subset2(x, : オブジェクト 'vs' がありません

# NSE
n = function(d, i) {
d[[deparse(substitute(i))]]
}

n(mtcars, "vs")
## NULL
n(mtcars, vs)
## [1] 0 0 1 1 0 1 0 1 1 1 1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 1 0 1 0 0 0 1

何がオトクかというと、引数に変数名を与えるときに""で囲わなくてよい、とか、まあ色いろあるんだけど、あんまり気にしないでいいと思う。

subset()subsetとかをNSEじゃない方法で与えるとか、結構面倒かも。

@dichika さんの元記事の例

# NSE

iris %>% group_by(Species) %>% summarise_each(funs(min, mean, median, max), Sepal.Width)
## Error in eval(expr, envir, enclos): 関数 "%>%" を見つけることができませんでした

# ふつー評価
iris %>% group_by(Species) %>% summarise_each_(funs(min, mean, median, max), "Sepal.Width")
## Error in eval(expr, envir, enclos): 関数 "%>%" を見つけることができませんでした

列名を変数で与えたいときなんかはふつー評価でやる。


以下余談。RではこのNSEが実に多用されているのである。

例えばsubset

subset(mtcars, subset = disp == max(disp))

## mpg cyl disp hp drat wt qsec vs am gear carb
## Cadillac Fleetwood 10.4 8 472 205 2.93 5.25 17.98 0 0 3 4

ここでdisp == max(disp)というのは引数の値ではなくて表現式で、

これを呼び出し元で評価しても失敗する。

なぜならdispなんていうオブジェクトはどこにも無いからで、それはmtcarsの中にしかない。

つまり、これは失敗する。

cond = disp == max(disp); subset(mtcars, subset = cond)

## Error in eval(expr, envir, enclos): オブジェクト 'disp' がありません
## Error in eval(expr, envir, enclos): オブジェクト 'cond' がありません

ちょっと凝ったことしてプロミス化しても

delayedAssign("cond", disp == max(disp))

subset(mtcars, subset = cond)
## Error in eval(expr, envir, enclos): オブジェクト 'disp' がありません

無駄。

ちなみにsubsetの列選択とか、もう無茶苦茶な感じがするが、これはこれで便利である。

head(subset(mtcars, select = c(mpg, wt:am)))

## mpg wt qsec vs am
## Mazda RX4 21.0 2.620 16.46 0 1
## Mazda RX4 Wag 21.0 2.875 17.02 0 1
## Datsun 710 22.8 2.320 18.61 1 1
## Hornet 4 Drive 21.4 3.215 19.44 1 0
## Hornet Sportabout 18.7 3.440 17.02 0 0
## Valiant 18.1 3.460 20.22 1 0