Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
276 views
in Technique[技术] by (71.8m points)

r - Programming-safe version of subset - to evaluate its condition while called from another function

As subset() manual states:

Warning: This is a convenience function intended for use interactively

I learned from this great article not only the secret behind this warning, but a good understanding of substitute(), match.call(), eval(), quote(), ?call, promise and other related R subjects, that are a little bit complicated.

Now I understand what's the warning above for. A super-simple implementation of subset() could be as follows:

subset = function(x, condition) x[eval(substitute(condition), envir=x),]

While subset(mtcars, cyl==4) returns the table of rows in mtcars that satisfy cyl==4, enveloping subset() in another function fails:

sub = function(x, condition) subset(x, condition)

sub(mtcars, cyl == 4)
# Error in eval(expr, envir, enclos) : object 'cyl' not found

Using the original version of subset() also produces exactly the same error condition. This is due to the limitation of substitute()-eval() pair: It works fine while condition is cyl==4, but when the condition is passed through the enveloping function sub(), the condition argument of subset() will be no longer cyl==4, but the nested condition in the sub() body, and the eval() fails - it's a bit complicated.

But does it exist any other implementation of subset() with exactly the same arguments that would be programming-safe - i.e. able to evaluate its condition while it's called by another function?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The [ function is what you're looking for. ?"[". mtcars[mtcars$cyl == 4,] is equivalent to the subset command and is "programming" safe.

sub = function(x, condition) {
 x[condition,]
}

sub(mtcars, mtcars$cyl==4)

Does what you're asking without the implicit with() in the function call. The specifics are complicated, however a function like:

sub = function(x, quoted_condition) {
  x[with(x, eval(parse(text=quoted_condition))),]
}

sub(mtcars, 'cyl==4')

Sorta does what you're looking for, but there are edge cases where this will have unexpected results.


using data.table and the [ subset function you can get the implicit with(...) you're looking for.

library(data.table)
MT = data.table(mtcars)

MT[cyl==4]

there are better, faster ways to do this subsetting in data.table, but this illustrates the point well.


using data.table you can also construct expressions to be evaluated later

cond = expression(cyl==4)

MT[eval(cond)]

these two can now be passed through functions:

wrapper = function(DT, condition) {
  DT[eval(condition)]
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...