Convert a variable into a string in R - r

I have a simple question. Suppose that I have this code:
y <- x
name <- "x"
Where x could be any R object.
Is there an a way the variable name to automatically take the string x once I assign x to y ?

If I understand correctly, you want the value of x to be assigned to y, while the variable name itself to be assigned to name as a string. If so, then you can capture x as an unevaluated expression, then 1) evaluate it and store the result to y, and 2) deparse it and store the resulting string to name:
z <- quote(x) # z contains unevaluated expression `x`
y <- eval(z) # evaluates the expression, returning the value of x
name <- deparse(z) # returns the expression as a string

If the question is how to get the name of the variable that was assigned to y then one cannot do that perfectly but a heuristic would be to examine every variable and return the name (or names) of anything with matching value.
If the code you are using is within a function and you are looking for variables that are defined in that function then use e <- environment() in place of the line that defines e below.
# test data
# start a fresh R session
a <- 1
x <- 2
y <- x
e <- .GlobalEnv
setdiff(names(Filter(isTRUE, eapply(e, identical, y))), "y")
## [1] "x"
Note
If the question is how to get value value of x from its name then:
Use get:
# test input
x <- 3
name <- "x"
y <- get(name)
y
## [1] 3
This will also work if x is in the global environment:
y <- .GlobalEnv[[name]]
y
## [1] 3

Would that work for you? I personally would not use anything like this in my scripts, but it accomplishes what you need:
assign_and_save_name <- function(new_var, var, var_name){
var_value <- sapply(ls(envir = parent.frame()), get)[[var]]
assign(new_var, var_value, parent.frame())
assign(var_name, var, parent.frame())
}
x <- 3
assign_and_save_name('y', 'x', 'name_x')

Related

Getting the name of the vector passed to a function in R

I think this best described by example. I have a function that does something and then returns a list. I want to be able to use the name of the variable that was passed to the function as the name of an element in the list that is to be returned. For example, my function is
myFun <- function(x) {
... do something here to generate some value
list(original name of x = some value)
}
CL <- c(1,2,3,4,5)
z <- myFun(CL)
I am passing CL to the function. Then I want the function to return the following:
z
$CL
[1] some value
How can I get that variable name CL from the pass?
Here an example that takes a value (x) and adds 2 to it, returning inside a list with name of the object passed to the function (myFUN).
myFUN <- function(x){
y <- x + 2
output <- list(y)
names(output) <- deparse(substitute(x))
return(output)
}
CL <- 3
myFUN(CL)
$CL
[1] 5

Chaining assignments in R

I recently discovered that R allows chaining of assignments, e.g.
a = b = 1:10
a
[1] 1 2 3 4 5 6 7 8 9 10
b
[1] 1 2 3 4 5 6 7 8 9 10
I then thought that this could also be used in functions, if two arguments should take the same value. However, this was not the case. For example, plot(x = y = 1:10) produces the following error: Error: unexpected '=' in "plot(x = y =". What is different, and why doesn't this work? I am guessing this has something to with only the first being returned to the function, but both seem to be evaluated.
What are some possibilities and constraints with chained assignments in R?
I don't know about "canonical", but: this is one of the examples that illustrates how assignment (which can be interchangeably be done with <- and =) and passing named arguments (which can only be done using =) are different. It's all about the context in which the expressions x <- y <- 10 or x = y = 10 are evaluated. On their own,
x <- y <- 10
x = y = 10
do exactly the same thing (there are few edge cases where = and <- aren't completely interchangeable as assignment operators, e.g. having to do with operator precedence). Specifically, these are evaluated as (x <- (y <- 10)), or the equivalent with =. y <- 10 assigns the value to 10, and returns the value 10; then x <- 10 is evaluated.
Although it looks similar, this is not the same as the use of = to pass a named argument to a function. As noted by the OP, if f() is a function, f(x = y = 10) is not syntactically correct:
f <- function(x, y) {
x + y
}
f(x = y = 10)
## Error: unexpected '=' in "f(x = y ="
You might be tempted to say "oh, then I can just use arrows instead of equals signs", but this does something different.
f(x <- y <- 10)
## Error in f(x <- y <- 10) : argument "y" is missing, with no default
This statement tries to first evaluate the x <- y <- 10 expression (as above); once it works, it calls f() with the result. If the function you are calling will work with a single, unnamed argument (as plot() does), and you will get a result — although not the result you expect. In this case, since the function has no default value for y, it throws an error.
People do sometimes use <- with a function call as shortcut; in particular I like to use idioms like if (length(x <- ...) > 0) { <do_stuff> } so I don't have to repeat the ... later. For example:
if (length(L <- list(...))>0) {
warning(paste("additional arguments to ranef.merMod ignored:",
paste(names(L),collapse=", ")))
}
Note that the expression length(L <- list(...))>0) could also be written as !length(L <- list(...)) (since the result of length() must be a non-negative integer, and 0 evaluates to FALSE), but I personally think this is a bridge too far in terms of compactness vs readability ... I sometimes think it would be better to forgo the assignment-within-if and write this as L <- list(...); if (length(L)>0) { ... }
PS forcing the association of assignment in the other order leads to some confusing errors, I think due to R's lazy evaluation rules:
rm(x)
rm(y)
## neither x nor y is defined
(x <- y) <- 10
## Error in (x <- y) <- 10 : object 'x' not found
## both x and y are defined
x <- y <- 5
(x <- y) <- 10
## Error in (x <- y) <- 10 : could not find function "(<-"

Parsing R function names, arguments, and return values

How can I programatically parse the names of functions, arguments, and their return values?
I am interested in generating workplan dataframes for automating R data analysis workflows with the drake package. One can generate such workplan dataframes with the workplan function.
I have an R script with functions that I would like to use. For example:
funA <- function(x){
y <- x + 2
y
}
funB <- function(y){
z <- y^2
z
}
And I would like to programmatically generate a dataframe like the one below. How can I parse function names, arguments, and return values, and create a data.frame like this, either with drake::workplan, or with other function?
target command
1 y funA(5)
2 z funB(3)
One would do this by hand like this:
my_plan <- drake::workplan(z=funB(5), y=funA(3))
And then run the workflow with:
drake::make(my_plan)
Thank you.
You can get the arguments with formals()
funA <- function(x, b = "default"){
y <- x + 2
y
}
formals(funA)
You can also extract the body and environment of a function with body() and environment()
There is no way to get the name of a function. A function doesn't really have a name, a name refers to a function (how would you even know what to refer to if you didn't know the name?).
There is also no way to get the return value. In your example, you could get the z and y by parsing the body() of the function manually, but this is a really bad idea and would only work if you write your function source in a specific way. Even if you did this, it makes no sense. z and y get destroyed when the function returns.
Maybe you could elaborate why exactly you need the return value and function namen, I am sure there is another way around what you are trying to achieve.
Assuming you have a source file like this:
funA <- function(x, y){
y <- x + 2
y
}
funB <- function(y){
z <- y^2
z
}
named test.r, you can do something like this:
library(purrr)
library(dplyr)
fenv <- new.env()
parse("test.r") %>%
keep(is.language) %>%
keep(~grepl(", function", toString(.x))) %>%
map(eval, envir=fenv) %>%
map_df(~{
params <- list(names(formals(.x)))
bdy <- deparse(body(.x))
bdy <- bdy[length(bdy)-1]
data_frame(target = trimws(bdy), params = params)
}) %>%
mutate(fname = ls(fenv))
which produces:
## # A tibble: 2 x 3
## target params fname
## <chr> <list> <chr>
## 1 y <chr [2]> funA
## 2 z <chr [1]> funB
That's fragile but not too fragile since it's filtering out language objects and functions before the eval and temporary environment assignment.
I'm making an assumption you can extract the parameter names from the params column to then ultimately generate what you need.

Dynamic variable names in function in R

I am looking to make a function that takes a vector as input, does some simple arithmetic with the vector and call the new vector something which consists of a set string (say, "log.") plus the original vector name.
d = c(1 2, 3)
my.function <- function { x
x2 <- log(x)
...
I would like the function to return a vector called log.d (that is, not log.x or something set, but something dependent on the name of the vector input as x).
You can try next:
d = c(1, 2, 3)
my.function <- function(x){
x2 <- log(x)
arg_name <- deparse(substitute(x)) # Get argument name
var_name <- paste("log", arg_name, sep="_") # Construct the name
assign(var_name, x2, env=.GlobalEnv) # Assign values to variable
# variable will be created in .GlobalEnv
}
One way to do this would be to store separately names of all your input vector names and then pass them to assign function. Like assign takes text string for output object name, get looks up object from string.
I will assume your vectors all follow common pattern and start with "d", to make it all as dynamic as possible.
d1 <- c(1,2,3)
d2 <- c(2,3,4)
vec_names <- ls(pattern = "^d")
log_vec <- function(x){
log(x)
}
sapply(vec_names, function(x) assign(paste0("log.", x), log_vec(get(x)), envir = globalenv()))
This should create two new objects "log.d1" and "log.d2".

Using paste and substitute in combination with quotation marks in R

Please note that I already had a look at this and that but still cannot solve my problem.
Suppose a minimal working example:
a <- c(1,2,3)
b <- c(2,3,4)
c <- c(4,5,6)
dftest <- data.frame(a,b,c)
foo <- function(x, y, data = data) {
data[, c("x","y")]
}
foo(a, b, data = dftest)
Here, the last line obviously returns an Error: undefined columns selected. This error is returned because the columns to be selected are x and y, which are not part of the data frame dftest.
Question: How do I need to formulate the definition of the function to obtain the desired output, which is
> dftest[, c("a","b")]
# a b
# 1 1 2
# 2 2 3
# 3 3 4
which I want to obtain by calling the function foo.
Please be aware that in order for the solution to be useful for my purposes, the format of the function call of foo is to be regarded fixed, that is, the only changes are to be made to the function itself, not the call. I.e. foo(a, b, data = dftest) is the only input to be allowed.
Approach: I tried to use paste and substitute in combination with eval to first replace the x and y with the arguments of the function call and then evaluate the call. However, escaping the quotation marks seems to be a problem here:
foo <- function(x, y, data = data) {
substitute(data[, paste("c(\"",x,"\",\"",y,"\")", sep = "")])
}
foo(a, b, data = dftest)
eval(foo(a, b, data = dftest))
Here, foo(a, b, data = dftest) returns:
dftest[, paste("c(\"", a, "\",\"", b, "\")", sep = "")]
However, when evaluating with eval() (focusing only on the paste part),
paste("c(\"", a, "\",\"", b, "\")", sep = "")
returns:
# "c(\"1\",\"2\")" "c(\"2\",\"3\")" "c(\"3\",\"4\")"
and not, as I would hope c("a","b"), thus again resulting in the same error as above.
Try this:
foo <- function(x, y, data = data) {
x <- deparse(substitute(x))
y <- deparse(substitute(y))
data[, c(x, y)]
}

Resources