Non standard evaluation from another function in R -
here example hadley's advanced r book:
sample_df <- data.frame(a = 1:5, b = 5:1, c = c(5, 3, 1, 4, 1)) subset2 <- function(x, condition) { condition_call <- substitute(condition) r <- eval(condition_call, x, parent.frame()) x[r, ] } scramble <- function(x) x[sample(nrow(x)), ] subscramble <- function(x, condition) { scramble(subset2(x, condition)) } subscramble(sample_df, >= 4) # error in eval(expr, envir, enclos) : object 'a' not found
hadley explains:
can see problem is? condition_call contains expression condition. when evaluate condition_call evaluates condition, has value >= 4. however, can’t computed because there’s no object called in parent environment.
i understand there no a
in parent env, but, eval(condition_call, x, parent.frame())
evals conditional_call in x (a data.frame used environment), enclosed parent.frame()
. long there column named a
in x, why should there problem?
tl;dr
when subset2()
called within subscramble()
, condition_call
's value symbol condition
(rather call a >= 4
results when called directly). subset()
's call eval()
searches condition
first in envir=x
(the data.frame sample_df
). not finding there, next searches in enclos=parent.frame()
does find object named condition
.
that object promise object, expression slot a >= 4
, evaluation environment .globalenv
. unless object named a
found in .globalenv
or further search path, evaluation of promise fails observed message that: error in eval(expr, envir, enclos) : object 'a' not found
.
detailed explanation
a nice way discover what's going wrong here insert browser()
call right before line @ subset2()
fails. way, can call both directly , indirectly (from within function), , examine why succeeds in first case , fails in second.
subset2 <- function(x, condition) { condition_call <- substitute(condition) browser() r <- eval(condition_call, x, parent.frame()) ## <- point of failure x[r, ] }
calling subset2() directly
when user calls subset2()
directly, condition_call <- substitute(condition)
assigns condition_call
"call" object containing unevaluated call a >= 4
. call passed in eval(expr, envir, enclos)
, needs first argument symbol evaluates object of class call
, name
, or expression
. far good.
subset2(sample_df, >= 4) ## called from: subset2(sample_df, >= 4) browse[1]> is(condition_call) ## [1] "call" "language" browse[1]> condition_call ## >= 4
eval()
sets work, searching values of symbols contained in expr=condition_call
first in envir=x
, (if needed) in enclos=parent.frame()
, enclosing environments. in case, finds symbol a
in envir=x
(and symbol >=
in package:base
) , completes evaluation.
browse[1]> ls(x) ## [1] "a" "b" "c" browse[1]> get("a", x) ## [1] 1 2 3 4 5 browse[1]> eval(condition_call, x, parent.frame()) ## [1] false false false true true
calling subset2() within subscramble()
within body of subscramble()
, subset2()
called this: subset2(x, condition)
. fleshed out, call equivalent subset2(x=x, condition=condition)
. because supplied argument (i.e. value passed formal argument named condition
) expression condition
, condition_call <- substitute(condition)
assigns condition_call
symbol object condition
. (understanding point pretty key understanding how nested call fails.)
since eval()
happy have symbol (aka "name") first argument, once again far good.
subscramble(sample_df, >= 4) ## called from: subset2(x, condition) browse[1]> is(condition_call) ## [1] "name" "language" "refobject" browse[1]> condition_call ## condition
now eval()
goes work searching unresolved symbol condition
. no column in envir=x
(the data.frame sample_df
) matches, moves on enclos=parent.frame()
complicated reasons, environment turns out evaluation frame of call subscramble()
. there, does find object named condition
.
browse[1]> ls(x) ## [1] "a" "b" "c" browse[1]> ls(parent.frame()) ## aha! here's object named "condition" ## [1] "condition" "x"
as important aside, turns out there several objects named condition
on call stack above environment browser()
called.
browse[1]> sys.calls() # [[1]] # subscramble(sample_df, >= 4) # # [[2]] # scramble(subset2(x, condition)) # # [[3]] # subset2(x, condition) # browse[1]> sys.frames() # [[1]] # <environment: 0x0000000007166f28> ## <- envt in `condition` evaluated # # [[2]] # <environment: 0x0000000007167078> # # [[3]] # <environment: 0x0000000007166348> ## <- current environment ## orient ourselves bit more browse[1]> environment() # <environment: 0x0000000007166348> browse[1]> parent.frame() # <environment: 0x0000000007166f28> ## both environments contain objects named 'condition' browse[1]> ls(environment()) # [1] "condition" "condition_call" "x" browse[1]> ls(parent.frame()) # [1] "condition" "x"
to inspect condition
object found eval()
(the 1 in parent.frame()
, turns out evaluation frame of subscramble()
) takes special care. used recover()
, pryr::promise_info()
shown below.
that inspection reveals condition
promise expression slot a >= 4
, environment .globalenv
. our search a
has point moved past sample_df
(where value of a
found), evaluation of expression slot fails (unless object named a
found in .globalenv
or somewhere else farther search path).
browse[1]> library(pryr) ## is_promise() , promise_info() browse[1]> recover() # # enter frame number, or 0 exit # # 1: subscramble(sample_df, >= 4) # 2: #2: scramble(subset2(x, condition)) # 3: #1: subset2(x, condition) # selection: 1 # called from: top level browse[3]> is_promise(condition) # [1] true browse[3]> promise_info(condition) # $code # >= 4 # # $env # <environment: r_globalenv> # # $evaled # [1] false # # $value # null # browse[3]> get("a", .globalenv) # error in get("a", .globalenv) : object 'a' not found
for 1 more piece of evidence promise object condition
being found in enclos=parent.frame()
, 1 can point enclos
somewhere else farther search path, parent.frame()
skipped during condition_call
's evaluation. when 1 that, subscramble()
again fails, time message condition
not found.
## compare browse[1]> eval(condition_call, x, parent.frame()) # error in eval(expr, envir, enclos) (from #4) : object 'a' not found browse[1]> eval(condition_call, x, .globalenv) # error in eval(expr, envir, enclos) (from #4) : object 'condition' not found
Comments
Post a Comment