More than 1 year has 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