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