函数内部使用“With”(包装器)

发布于 2024-12-19 09:32:50 字数 813 浏览 3 评论 0原文

我想围绕自定义函数编写一个包装器,该函数将一些向量作为输入(例如:mtcars$hpmtcars$am等)以将输入作为数据帧名称(作为 data 参数,例如:mtcars)和变量名称(例如:hpam),像大多数标准功能一样。

但我有一些问题,我提出的“演示”函数(围绕 mean 的包装器不起作用。

代码:

f <- function(x, data=NULL) {
    if (!missing(data)) {
        with(data, mean(x))
    } else {
        mean(x)
    }
}

针对向量运行当然可以工作:

> f(mtcars$hp)
[1] 146.69

但不幸的是 with 失败了:

> f(hp, mtcars)
Error in with(d, mean(x)) : object 'hp' not found

在全局环境中/没有我的自定义函数时可以正常工作:

> with(mtcars, mean(hp))
[1] 146.69

我尝试使用 substitutedeparse 等进行一些实验,但没有任何成功,欢迎任何提示。 !

I would like to write a wrapper around a custom function that takes some vectors as input (like: mtcars$hp, mtcars$am etc.) to take input as data frame name (as data parameter, eg.: mtcars) and variable names (like: hp and am), as usual in most standard function.

But I have some problems, my proposed 'demo' function (a wrapper around mean does not work.

Code:

f <- function(x, data=NULL) {
    if (!missing(data)) {
        with(data, mean(x))
    } else {
        mean(x)
    }
}

Running against a vector works of course:

> f(mtcars$hp)
[1] 146.69

But with fails unfortunatelly:

> f(hp, mtcars)
Error in with(d, mean(x)) : object 'hp' not found

While in global environment/without my custom function works right:

> with(mtcars, mean(hp))
[1] 146.69

I have tried to do some experiment with substitute, deparse and others, but without any success. Any hint would be welcomed!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦里梦着梦中梦 2024-12-26 09:32:50

这是难题的关键部分:

f <- function(x,data=NULL) {
  eval(match.call()$x,data) # this is mtcars$hp, so just take the mean of it or whatever
}

> f(hp,mtcars)
 [1] 110 110  93 110 175 105 245  62  95 123 123 180 180 180 205 215 230  66  52  65  97 150 150 245 175  66
[27]  91 113 264 175 335 109

# it even works without a data.frame specified:
> f(seq(10))
 [1]  1  2  3  4  5  6  7  8  9 10

请参阅 @Andrie 指向 @Hadley 文档的链接,了解其工作原理的解释。请参阅 @Hadley 的注释以了解重要警告: f() 无法从另一个函数内部运行。

基本上,R 使用惰性求值(例如,在实际使用事物之前它不会求值)。因此,您可以通过传递它 hp 来避免这种情况,因为在它出现在某个地方之前,它仍然是一个未计算的符号。由于 match.call 将其作为符号获取并等待对其求值,所以一切都很好。

然后eval在指定的环境中对其进行评估。根据?eval,第二个参数表示:

要计算 expr 的环境。也可能为 NULL,
列表、数据帧、配对列表或 sys.call 指定的整数。

因此,无论是 NULL (如果您没有传递 data.frame)还是 data.frame,您都处于良好状态。

惰性求值的证明是,这不会返回错误(因为函数中从未使用 x):

> g <- function(x) {
+   0
+ }
> g(hp)
[1] 0

Here's the key piece of the puzzle:

f <- function(x,data=NULL) {
  eval(match.call()$x,data) # this is mtcars$hp, so just take the mean of it or whatever
}

> f(hp,mtcars)
 [1] 110 110  93 110 175 105 245  62  95 123 123 180 180 180 205 215 230  66  52  65  97 150 150 245 175  66
[27]  91 113 264 175 335 109

# it even works without a data.frame specified:
> f(seq(10))
 [1]  1  2  3  4  5  6  7  8  9 10

See @Andrie's link to @Hadley's document for an explanation of why it works. See @Hadley's note for a critical caveat: f() cannot be run from inside another function.

Basically R uses lazy evaluation (e.g. it doesn't evaluate things until they're actually used). So you can get away with passing it hp because it remains an unevaluated symbol until it appears somewhere. Since match.call grabs it as a symbol and waits to evaluate it, all is well.

Then eval evaluates it in the specified environment. According to ?eval, the second argument represents:

The environment in which expr is to be evaluated. May also be NULL, a
list, a data frame, a pairlist or an integer as specified to sys.call.

Therefore you're in good shape with either NULL (if you're not passing a data.frame) or a data.frame.

Proof of lazy evaluation is that this doesn't return an error (since x is never used in the function):

> g <- function(x) {
+   0
+ }
> g(hp)
[1] 0
去了角落 2024-12-26 09:32:50
f <- function(x, data=NULL) {
    if (!missing(data)) { colname=deparse(substitute(x))
         mean(data[[colname]])
    } else {
        mean(x)
    }
}

 f(hp, mtcars)
[1] 146.6875

(诚​​然,不像 @gsk 那样紧凑,我想我会尝试记住他的方法而不是我的方法。感谢 Josh O'Brien 指出了一个现已修复的错误。)

f <- function(x, data=NULL) {
    if (!missing(data)) { colname=deparse(substitute(x))
         mean(data[[colname]])
    } else {
        mean(x)
    }
}

 f(hp, mtcars)
[1] 146.6875

(Admittedly not as compact as @gsk's and I think I will try to remember his method over mine. And thanks to Josh O'Brien for pointing out an error that's now been fixed.)

自控 2024-12-26 09:32:50

试试这个:

f <- function(x, data = NULL) {
     if (is.null(data)) {
         mean(x)
     } else { 
         attach(data)
         mean(x)
         detach(data)
     }
 }

同样在您的示例中,您输入数据集而不是列。
你的例子应该是 f(hp, mtcars)

try this:

f <- function(x, data = NULL) {
     if (is.null(data)) {
         mean(x)
     } else { 
         attach(data)
         mean(x)
         detach(data)
     }
 }

Also in your example you enter the data set instead of the column.
Your example should be f(hp, mtcars)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文