Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to dynamically call a variable?

Tags:

r

I'm working with three experiments that I store in three lists: exp1, exp2 and exp3. Each list has several items (name, conditions, dataset_a, dataset_b, etc.). Sometimes I want to perform an operation on all the experiments. Is there a way to store their names in a variable and call them dynamically? This doesn't work:

all_exp <- list(exp1=exp1,exp2=exp2,exp3=exp3)

Because if I later add something to the experiments, all_exp has a hard copy of the experiments at the previous state, not a reference.

This sort of works:

all_exp_names <- c("exp1","exp2","exp3")
all_exp <- lapply(all_exp_names, function(exp_name) (eval(parse(text=exp_name))))

but there must be a simpler way, and besides, the returned list loses the experiment names.

like image 373
nachocab Avatar asked Jan 05 '13 17:01

nachocab


People also ask

How to declare dynamic variables in Python?

I came up with three ways to do this in Python. 1. Using the exec command In the above program, I initially declared an empty dictionary and added the elements into the dictionary. I used string concatenation method to declare dynamic variables like x1, x2, x3……. . Then I assumed the values from the for loop to the dynamic variables.

Can You dynamically call a function in Python?

Dynamically calling functions in Python... Safely Before I started writing Python code, I spent most of my time working with PHP. There are a few things that PHP makes first class citizens to the language. One of them is dynamic class inclusion, and the other is the ability to dynamically call a function.

Is code that uses dynamic function calls too confusing?

Code that uses dynamic function calls can become confusing very fast. Simple is better than complex. Complex is better than complicated... Readability counts. What is a Dynamic Function Call?

Why doesn't Python have more dynamic functions?

However, in Python neither of these are very common. I think Python's focus on being explicit keeps developers from moving toward meta and magical programming. Python also provides decorators that can be used to explicitly hook or wrap a function. These two things limit the use of dynamic function calls.


Video Answer


2 Answers

If you want to keep the names, you could use sapply(..., simplify=FALSE, USE.NAMES=TRUE):

A <- B <- C <- 1:4
nms <- c("A", "B", "C")

sapply(nms, get, simplify=FALSE, USE.NAMES=TRUE)
## $A
## [1] 1 2 3 4
## 
## $B
## [1] 1 2 3 4
## 
## $C
## [1] 1 2 3 4
like image 88
Josh O'Brien Avatar answered Sep 23 '22 18:09

Josh O'Brien


I think you want

lapply(all_exp_names,get)

but that doesn't actually return a named list. You can do

setNames(lapply(all_exp_names,get),all_exp_names)

which is admittedly a little clunky, although you could package it in a function if you use it frequently (see @JoshOBrien's answer for a slightly better solution to this).

The more idiomatic thing to do, I think, would be simply to keep your reference copies of the data in a named list in the first place.

edit: my original delayed assignment/evaluation code below was clever, but completely missed the point that R has a built-in delayedAssign function, which does the same thing (but probably more robustly) as my makeDelayVar function below:

delayedAssign("exp_all",list(exp1=exp1,exp2=exp2))

(Thanks to do.call and curve can not plot a function inside another function environment for pointing this out.) The trick of defining an infix operator %<% as shown below might still be handy, though.


If you really want delayed assignment, this works (but not simply):
makeActiveBinding("all_exp",function() list(exp1=exp1,exp2=exp2), .GlobalEnv)
exp1 <- 2
exp2 <- 3
all_exp
## $exp1
## [1] 2
##  
## $exp2
## [1] 3

You could wrap this in a makeDelayVar function too, although you might have to be careful about the evaluation environment.

makeDelayVar <- function(var,val) {
   makeActiveBinding(deparse(substitute(var)), function() val, parent.frame())
}
makeDelayVar(all_exp, list(exp1=exp1,exp2=exp2))
all_exp

This works the same as above (you can remove exp1 and exp2, define all_exp, and then re-define exp[12] if you want to confirm that this procedure is really doing delayed evaluation).

To get even sillier, you can define %<% to do delayed assignment (R allows infix operators to be defined as %[character]%):

`%<%` <- makeDelayVar
all_exp %<% list(exp1,exp2)

I would use this with caution, though -- it could be fragile in some circumstances. For example, restrict it to interactive contexts where you will know right away if it breaks or does something funny, and don't try to be clever passing the results of delayed evaluation as arguments to functions etc.

like image 44
Ben Bolker Avatar answered Sep 26 '22 18:09

Ben Bolker