Using eval parse to assign variable name in a function

All,
I have a relatively simple question - when I have multiple data sets and I wish to apply multiple functions on these data sets within a loop, how can I reference these data sets using a variable. For example:

# Generate some test data 
Test1 <- runif(10,min = 0,max = 150)
Test2 <- runif(10,min = 0,max = 150)
Test3 <- runif(10,min = 0,max = 150)

# Access the data sets using a variable within loop 
 for (i in seq(1, 3, 1)) 
 { 
     print(i)
     print(eval(parse(paste0("Test",i))))
 }

The above piece of code yields the following error :

**Error in file(filename, "r") : cannot open the connection
In addition: Warning message:
In file(filename, "r") :
cannot open file 'Test1': No such file or directory
**

Hi,

not sure exactly if it will answer your question but to access the data of Test1 in the first iteration of the for loop and then Test2... you can use that

get(eval(paste0("Test",i)))

You can make it work by passing the strings to the text parameter of parse, which otherwise assumes a string is a filepath.

set.seed(47)

Test1 <- runif(5)
Test2 <- runif(5)
Test3 <- runif(5)

for (i in seq(3)) { 
    print(i)
    print(eval(parse(text = paste0("Test",i))))
}
#> [1] 1
#> [1] 0.9769620 0.3739160 0.7615020 0.8224916 0.5735444
#> [1] 2
#> [1] 0.6914124 0.3890619 0.4689460 0.5433097 0.9248920
#> [1] 3
#> [1] 0.1387976 0.7019872 0.1621936 0.5993070 0.5060361

...but eval(parse(...)) is a discouraged idiom unless you're operating on the language, which is a decidedly advanced topic. for loops are rarely necessary in R, either; you can either use vectorized operations or iterate with lapply and friends.

A more elegant way to collect the results would be with get (which looks for a single variable with a name matching the passed string) or mget (which looks for a vector of names and returns a list):

mget(paste0('Test', 1:3))
#> $Test1
#> [1] 0.9769620 0.3739160 0.7615020 0.8224916 0.5735444
#> 
#> $Test2
#> [1] 0.6914124 0.3890619 0.4689460 0.5433097 0.9248920
#> 
#> $Test3
#> [1] 0.1387976 0.7019872 0.1621936 0.5993070 0.5060361

Neither is good style, though. The right solution is to start with what mget returns: a named list:

test <- lapply(1:3, function(x) runif(5))
names(test) <- paste0('Test', 1:3)
test
#> $Test1
#> [1] 0.90197352 0.40050280 0.03094497 0.07135816 0.46831653
#> 
#> $Test2
#> [1] 0.1781453 0.5567963 0.5157950 0.1334304 0.6892834
#> 
#> $Test3
#> [1] 0.38002493 0.03375759 0.03689426 0.53644158 0.79955139

...which is then easy to iterate over with lapply or the like without trying to wrangle names.

3 Likes

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.