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

Popular posts from this blog

angularjs - ADAL JS Angular- WebAPI add a new role claim to the token -

php - CakePHP HttpSockets send array of paramms -

node.js - Using Node without global install -