How to get name of variable in R (substitute)?
Asked Answered
F

3

22

I stacked with trying to pass variable through few functions, and on the final function I want to get the name of the original variable. But it seems like substitute function in R looked only in "local" environment, or just for one level up. Well, let me explain it by code:

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"

Well, it seems like we printing the name of variable that only pass to the fun1. Documentation of the substitute tells us, that we can use env argument, to specify where we can look. But by passing .Global or .BaseNamespaceEnv as an argument to substitute I got even more strange results - "some_variable"

I believe that answer is in this function with using env argument, so, could you please explain me how it works and how can I get what I need. Thanks in advance!

Ferris answered 19/6, 2014 at 15:5 Comment(8)
Pretty sure "The R-Inferno" touches on this. BTW, I'm assuming you meant to call fun1 inside fun2 with var_pass ?Fluke
Oh, thanks! Yes, I meant fun1(var_pass).Ferris
Why do you need the name in the first place. There is probably a good, simpler alternative.Willhite
Well, I do a lot of computation for the data.frame, but in the end I would like to get access to the list (my_list$my_var), to get some data, that related to the data.frame. Probably there is some way to do it, I actually done what I need, but it was really messy. I put the name of variable as additional column, but with big datasets it is really slow down the processFerris
If you need data inside a function, simply pass it on as an input argument. Similarly, if you need anything from within the function outside the function, use the return argument. Does this suit your needs?Willhite
Thanks for suggestion, but not really. But I will think about this way. Thanks anyway! ))Ferris
If you want better feedback, I would recommend you add more information as to what you are trying to accomplish, and why my suggestion does not work.Willhite
You might find adv-r.had.co.nz/Computing-on-the-language.html helpful - it explains the problem and how you should design your functions to avoid it.Monopoly
P
29

I suggest you consider passing optional name value to these functions. I say this because it seems like you really want to use the name as a label for something in the end result; so it's not really the variable itself that matters so much as its name. You could do

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"

This way if you end up having some odd variable name and want to give a better name to a result, you at least have the option. And by default it should do what you want without having to require the name parameter.

Parasynthesis answered 19/6, 2014 at 15:34 Comment(3)
This one is interesting! )) But the problem I think in the R itself. I would even in the init() step add name parameter, but as I've already said, it is not the comfortable way with data.framesFerris
I don't undetstand your comment @EvilAnton. I'm not sure what the init() step is or how this specifically relates to data.frames. If you want to use name to extract a column from a data.frame, you can use df[, name] if the value in name exactly matches one of the column names.Parasynthesis
Init step I meant. When you load all data to the workspace. But actually, you made a good point. If to rename some of the column names that it should work.Ferris
B
3

Another approach I'd like to suggest is to use rlang::enexpr. The main advantage is that we don't need to carry the original variable name in a parameter. The downside is that we have to deal with expressions which are slightly trickier to use.

> 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

The trick here is that we have to evaluate the argument name in fun2 and build the call to fun1 as a character. If we were to simply call fun1 with enexpr(var_pass), we would loose the notion of fun2's variable name, because enexpr(var_pass) would never be evaluated in 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)

On top of that, note that neither fun1 nor fun2 return variable names as character vectors. The returned object is of class name (and can of course be coerced to character). The bright side is that you can use eval directly on it.

> ret <- fun2(my_var)
#Entering fun2
#Entering fun1
> as.character(ret)
[1] "my_var"
> class(ret)
[1] "name"
> eval(ret)
[1] 1 2
Blackdamp answered 6/1, 2020 at 8:59 Comment(0)
F
2

One hack, probably not the best way:

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

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

fun2(my_var)
# "my_var"

And you could run get on that. But as Paul H, suggests, there are better ways to track variables.

Fluke answered 19/6, 2014 at 15:21 Comment(2)
If somebody can lead me where to look to find the better way I would really appreciate it :)Ferris
Well, one way I see to make what I need happened, is to pass char all the time through functions and just use get(). But it is not looked good for me, at least from the way of programming architecture.Ferris

© 2022 - 2024 — McMap. All rights reserved.