在 R 中将变量名传递到函数中

42

我注意到很多程序包允许你传递符号名称,即使在调用函数的上下文中可能无效。 我想知道这是如何工作的,以及如何在我的代码中使用它?

以下是ggplot2的一个示例:

a <- data.frame(x=1:10,y=1:10)
library(ggplot2)
qplot(data=a,x=x,y=y)

xy 不在我的命名空间中,但 ggplot 理解它们是数据框的一部分,并将它们的评估推迟到它们有效的上下文中。我尝试过做同样的事情:

b <- function(data,name) { within(data,print(name)) }
b(a,x)

然而,这个方法彻底失败了:

Error in print(name) : object 'x' not found

我做错了什么?这个怎么工作?

注意: 这不是在 R 中将变量名传递给函数的重复问题

4个回答

43

我最近发现了一种更好的传递变量名称的方法。

a <- data.frame(x = 1:10, y = 1:10)

b <- function(df, name){
    eval(substitute(name), df)
}

b(a, x)
  [1]  1  2  3  4  5  6  7  8  9 10

更新 这种方法使用非标准的评估方式。我开始解释,但很快意识到Hadley Wickham比我做得更好。请阅读这篇http://adv-r.had.co.nz/Computing-on-the-language.html


1
你能详细解释一下吗?(添加一些注释)这个方法听起来很有趣。请注意,Stack Overflow 上没有“旧线程”,信息被保存下来供像你和我这样通过谷歌搜索问题的人查询。 - static_rtti
2
这是一个非常好的答案,谢谢。如果有帮助的话,那么如果 var <- eval(substitute(var), data) 给你对象,那么 var.name <- substitute(var) 将会给你传递到函数中的变量名。 - drstevok

18

例如,您可以使用match.call 来实现此操作:

b <-  function(data,name) {

  ## match.call return a call containing the specified arguments 
  ## and the function name also 
  ## I convert it to a list , from which I remove the first element(-1)
  ## which is the function name

  pars <- as.list(match.call()[-1])
  data[,as.character(pars$name)]

}

 b(mtcars,cyl)
 [1] 6 6 4 6 8 6 8 4 4 6 6 8 8 8 8 8 8 4 4 4 4 8 8 8 8 4 4 4 8 6 8 4

解释:

match.call返回一个函数调用,其中所有指定的参数都是通过它们的全名指定的。

所以这里match.call的输出有两个符号:

b <-  function(data,name) {
  str(as.list(match.call()[-1]))  ## I am using str to get the type and name
}

b(mtcars,cyl)
List of 2
 $ data: symbol mtcars
 $ name: symbol cyl

然后我使用第一个符号mtcars并将第二个转换为字符串:

mtcars[,"cyl"]

或同等于:

eval(pars$data)[,as.character(pars$name)]

谢谢,现在清楚多了。但我仍希望能够理解为什么我的版本失败了。 - static_rtti
2
@static_rtti 所以不够清晰 :) 它失败是因为你的数据没有包含名为“name”的列。你应该使用类似于 within(data,pars$name) 的语句,其中pars是如上所述使用match.call定义的。 - agstudy
好的,我现在明白了。 "name" 只是函数参数的本地名称,所以我需要以某种方式 "取消引用" 它,这就是 match.call 的作用。 - static_rtti
也许是最后一个问题:为什么在match.call之后需要使用[-1]?它到底是什么意思? - static_rtti
@static_rtti 我在我的答案中添加了一些注释来解释-1的含义。这不是R语言中最容易的部分。尝试阅读什么是“调用”。 - agstudy
非常感谢您提供的所有答案。我知道这些是语言中的黑暗角落,但我认为如果我想从表面上的理解转向真正的理解,我必须经历这个过程。 - static_rtti

6

虽然这个帖子很老,但是你也可以使用get命令。对我来说使用这个命令效果更好。

a <- data.frame(x = 1:10, y = 11:20)

b <- function(df, name){

   get(name, df)

 }

b(a, "x")
 [1]  1  2  3  4  5  6  7  8  9 10 

3

如果在调用函数时将变量名放在引号中,它就有效:

> b <- function(data,name) { within(data,print(name)) }
> b(a, "x")
[1] "x"
    x  y
1   1  1
2   2  2
3   3  3
4   4  4
5   5  5
6   6  6
7   7  7
8   8  8
9   9  9
10 10 10

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接