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.
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.
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.
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?
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.
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
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With