如何在R中获取变量的名称(substitute)?

20

我在尝试通过几个函数传递变量时遇到了问题,在最后一个函数中,我想获取原始变量的名称。但是似乎R中的替代函数仅在“本地”环境中查找,或者只查找上一级。好吧,让我用代码来解释一下:

fun1 <- function (some_variable) {deparse(substitute(some_variable)}
fun2 <- function (var_pass) { fun1 (var_pass) }
my_var <- c(1,2) # I want to get 'my_var' in the end
fun2 (my_var) # > "var_pass"

看起来我们正在打印仅传递给fun1的变量名称。替代文档告诉我们,可以使用env参数指定查找的位置。但是通过将 .Global .BaseNamespaceEnv 作为substitute的参数进行传递,我得到了更奇怪的结果 - "some_variable"。

我相信答案在这个带有env参数的函数中,所以,您能否请解释一下它的工作原理以及如何获得我需要的内容。谢谢!


哦,谢谢!是的,我指的是 fun1(var_pass)。 - EvilAnton
你为什么需要这个名字呢?可能有更好、更简单的替代方案。 - Paul Hiemstra
嗯,我对数据框进行了很多计算,但最终我想要访问列表(my_list$my_var),以获取与数据框相关的一些数据。可能有某种方法可以做到这一点,我实际上已经完成了我需要的工作,但它非常混乱。我将变量名作为附加列放置,但对于大型数据集,这会使过程变得非常缓慢。 - EvilAnton
2
如果你需要函数内的数据,只需将其作为输入参数传递即可。同样地,如果你需要从函数内部获取任何东西并在函数外使用,可以使用返回参数。这符合你的需求吗? - Paul Hiemstra
如果您想获得更好的反馈,我建议您添加更多信息,说明您试图实现什么以及为什么我的建议不起作用。 - Paul Hiemstra
显示剩余3条评论
3个回答

25

我建议您考虑将可选的名称值传递给这些函数。我之所以这么说是因为似乎您真的想将名称用作最终结果中某些内容的标签,因此重要的并不是变量本身,而是它的名称。

fun1 <- function (some_variable, name=deparse(substitute(some_variable))) {
    name
}
fun2 <- function (var_pass, name=deparse(substitute(var_pass))) { 
    fun1 (var_pass, name) 
}
my_var <- c(1,2)

fun2(my_var)
# [1] "my_var"

fun1(my_var)
# [1] "my_var"

这样,如果你最终得到一些奇怪的变量名并想要给结果一个更好的名字,你至少有这个选项。默认情况下,它应该按照你想要的方式运行,而无需使用名称参数。


这个很有趣!)) 但我认为问题在于 R 本身。即使在 init() 步骤中添加 name 参数,但正如我已经说过的那样,这并不是使用 data.frames 的舒适方式。 - EvilAnton
1
我不明白你的评论 @EvilAnton。我不确定 init() 步骤是什么,或者它如何特别与数据框架相关联。如果您想使用 name 从数据框架中提取列,则如果 name 的值恰好匹配其中一个列名,则可以使用 df[, name] - MrFlick
初始化步骤是指将所有数据加载到工作区。但实际上,你说得很对。如果重命名一些列名,那么它应该可以工作。 - EvilAnton

3

我想提出的另一种方法是使用rlang::enexpr。 主要优点是我们不需要在参数中携带原始变量名。缺点是我们必须处理稍微棘手的表达式。

> fun1 <- function (some_variable) {
    message("Entering fun1")
    rlang::enexpr(some_variable)
}
> fun2 <- function (var_pass) {
    message("Entering fun2")
    eval(parse(text=paste0("fun1(", rlang::enexpr(var_pass), ")")))
}
> my_var <- c(1, 2)
> fun1(my_var)
#Entering fun1
my_var
> fun2(my_var)
#Entering fun2
#Entering fun1
my_var

这里的诀窍在于我们需要在fun2中评估参数名称,并将对fun1的调用构建为character。如果我们简单地使用enexpr(var_pass)调用fun1,我们将失去fun2变量名称的概念,因为enexpr(var_pass)永远不会在fun2中被评估:
> bad_fun2 <- function (var_pass) {
    message("Entering bad fun2")
    fun1(rlang::enexpr(var_pass))
}
> bad_fun2(my_var)
#Entering bad fun2
#Entering fun1
rlang::enexpr(var_pass)

此外,请注意,fun1fun2都不会返回变量名称作为字符向量。返回的对象是name类(当然也可以强制转换为character)。
好消息是,你可以直接在其上使用eval
> ret <- fun2(my_var)
#Entering fun2
#Entering fun1
> as.character(ret)
[1] "my_var"
> class(ret)
[1] "name"
> eval(ret)
[1] 1 2

2

一种可能不是最好的方法:

fun2 <- function (var_pass) { fun1 (deparse(substitute(var_pass))) }

fun1 <- function (some_variable) {(some_variable))}

fun2(my_var)
# "my_var"

您可以对其运行get。但是,就像Paul H所建议的那样,有更好的方法来跟踪变量。


如果有人能指导我在哪里寻找更好的方法,我会非常感激 :) - EvilAnton
我认为实现我所需的一种方法是通过函数传递char并仅使用get()。但从编程架构的角度来看,这对我来说不太好。 - EvilAnton

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