アップデート:
以下は、2 つの問題を修正した新しいバージョンです。
a) 以前のバージョンは単純sys.frames()
に逆方向にトラバースしました。このバージョンはparent.frames()
に達するまで続きます.GlobalEnv
。これは、たとえばのフレームを無視する場合subscramble
に重要です。scramble
b) このバージョンには、substitute
レベルごとに 1 つのみがあります。これにより、2 番目のsubstitute
呼び出しが、最初の呼び出しによって導入された 1 つ上のレベルのシンボルに置き換わることがなくなりsubstitute
ます。
subset <- function(x, condition) {
call <- substitute(condition)
frames <- sys.frames()
parents <- sys.parents()
# starting one frame up, keep climbing until we get to .GlobalEnv
i <- tail(parents, 1)
while(i != 0) {
f <- sys.frames()[[i]]
# copy x into f, except for variable with conflicting names.
xnames <- setdiff(ls(x), ls(f))
for (n in xnames) assign(n, x[[n]], envir=f)
call <- eval(substitute(substitute(expr, f), list(expr=call)))
# leave f the way we found it
rm(list=xnames, envir=f)
i <- parents[i]
}
r <- eval(call, x, .GlobalEnv)
x[r, ]
}
このバージョンは、コメントからの @hadley のテストに合格しています。
mtcars $ condition <- 4; subscramble(mtcars, cyl == 4)
残念ながら、次の 2 つの例は異なる動作をするようになりました。
cyl <- 6; subset(mtcars, cyl==4)
local({cyl <- 6; subset(mtcars, cyl==4)})
これは Josh の最初の関数を少し変更したものです。スタック内の各フレームで、フレームから置換するx
前に置換します。これは、データ フレーム内のシンボルがすべてのステップで優先されることを意味します。ループ内で のフレームを_dat
スキップすることで、疑似 gensyms を回避できます。subset
for
subset <- function(x, condition) {
call <- substitute(condition)
frames <- rev(sys.frames())[-1]
for(f in frames) {
call <- eval(substitute(substitute(expr, x), list(expr=call)))
call <- eval(substitute(substitute(expr, f), list(expr=call)))
}
r <- eval(call, x, .GlobalEnv)
x[r, ]
}
このバージョンは単純なケースで機能します (回帰が発生していないことを確認する価値があります)。
subset(mtcars, cyl == 4)
# mpg cyl disp hp drat wt qsec vs am gear carb
# Datsun 710 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1
# Merc 240D 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2
# Merc 230 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2
# Fiat 128 32.4 4 78.7 66 4.08 2.200 19.47 1 1 4 1
# Honda Civic 30.4 4 75.7 52 4.93 1.615 18.52 1 1 4 2
# Toyota Corolla 33.9 4 71.1 65 4.22 1.835 19.90 1 1 4 1
# Toyota Corona 21.5 4 120.1 97 3.70 2.465 20.01 1 0 3 1
# Fiat X1-9 27.3 4 79.0 66 4.08 1.935 18.90 1 1 4 1
# Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.70 0 1 5 2
# Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.90 1 1 5 2
# Volvo 142E 21.4 4 121.0 109 4.11 2.780 18.60 1 1 4 2
また、 と で動作しsubscramble
ますf
:
scramble <- function(x) x[sample(nrow(x)), ]
subscramble <- function(x, condition) scramble(subset(x, condition))
subscramble(mtcars, cyl == 4) $ cyl
# [1] 4 4 4 4 4 4 4 4 4 4 4
f <- function() {cyl <- 4; g()}
g <- function() subset(mtcars, cyl == 4) $ cyl
g()
# [1] 4 4 4 4 4 4 4 4 4 4 4
さらに、いくつかのトリッキーな状況でも機能します。
gear5 <- function(z, condition) {
x <- 5
subset(z, condition & (gear == x))
}
x <- 4
gear5(mtcars, cyl == x)
# mpg cyl disp hp drat wt qsec vs am gear carb
# Porsche 914-2 26.0 4 120.3 91 4.43 2.140 16.7 0 1 5 2
# Lotus Europa 30.4 4 95.1 113 3.77 1.513 16.9 1 1 5 2
for
ループ内の行については、説明が必要な場合があります。次call
のように割り当てられているとします。
call <- quote(y == x)
str(call)
# language y == x
inの値4
を代入します。しかし、シンボル ではなく の内容が必要なため、単純な方法は機能しません。x
call
call
call
substitute(call, list(x=4))
# call
substitute
そのため、別の呼び出しを使用して、必要な式を作成します。
substitute(substitute(expr, list(x=4)), list(expr=call))
# substitute(y == x, list(x = 4))
これで、やりたいことを説明する言語オブジェクトができました。実際にそれを行うために残っているのは次のとおりです。
eval(substitute(substitute(expr, list(x=4)), list(expr=call)))
# y == 4