Force lapply/sapply to index output list with inputs

44 Views Asked by At

As in the title, how to force lapply or sapply to use input vector values to index the output. I have two cases; let me begin with the simpler one. The following code

lapply(c('a', 'b'), function(idx){
    rnorm(1)
})

gives me the output

[[1]]
[1] -1.359386

[[2]]
[1] -0.3428958

but I would like to receive:

$a
[1] -1.359386

$b
[1] -0.3428958

In the final aim, my code looks like follows:

m = sapply(unique(isotopes[, 1]), function(el){
    sapply(isotopes[isotopes$element == el, 2], function(mnr){
        isotopes[isotopes$element == el & isotopes$mass_nr == mnr, 3]
    })
})

and it gives me outputs like

$Pu
[1] 238.0496 239.0522 240.0538 241.0569 242.0587 244.0642

while I would like to obtain list of lists, to be able to use m['Pu'][240] or m['Pu']['240'] etc. (for 'Pu' isotopes[isotopes$element == el, 2] is a vector of 238 239 240 241 242 244)

PS. Of course, I can use

m = function(el, mnr){ isotopes[isotopes$element == el & isotopes$mass_nr == mnr, 3] }

but I'm interested if the above problem possesses a neat solution :)

3

There are 3 best solutions below

0
ThomasIsCoding On BEST ANSWER

You could try

lapply(c(a = "a", b = "b"), function(idx) {
    rnorm(1)
})

which should give the desired output

$a
[1] 1.556433

$b
[1] -0.2282002
0
Rui Barradas On

If you want to split the data set twice, use split twice, it returns a named list.

In the code below I have used a column name third_col, not the column number.

set.seed(2023)
n <- 20L
isotopes <- data.frame(
  element = sample(letters[1:2], n, TRUE),
  mass_nr = sample(3, n, TRUE),
  third_col = rnorm(n)
)


sp <- split(isotopes, isotopes$element) 
lapply(sp, \(x) split(x$third_col, x$mass_nr))
#> $a
#> $a$`1`
#> [1]  0.8602705 -0.1299832  0.1092238  0.1615685 -1.5131984  1.1431521
#> 
#> $a$`2`
#> [1] -0.4709085 -0.4389865 -1.1927195 -1.1326977
#> 
#> $a$`3`
#> [1]  0.8468178 -1.3264019  0.9712922
#> 
#> 
#> $b
#> $b$`1`
#> [1] 1.9957190 0.1108168
#> 
#> $b$`2`
#> [1]  0.7522100  0.9254184 -0.2917223
#> 
#> $b$`3`
#> [1] -0.2294296 -0.7137467

Created on 2023-09-20 with reprex v2.0.2

Or with a pipe from the first split to a lapply/split. Then extract a couple of named list members.

m <- split(isotopes, isotopes$element) |> lapply(\(x) split(x$third_col, x$mass_nr))

m$a[['2']]
#> [1] -0.4709085 -0.4389865 -1.1927195 -1.1326977

m$b$`3`
#> [1] -0.2294296 -0.7137467

Created on 2023-09-20 with reprex v2.0.2

1
Mark On

Another way - use sapply() with the option simplify = FALSE to get a list as output:

sapply(c('a', 'b'), \(idx) rnorm(1), simplify = FALSE)

Output:

$a
[1] 0.4824588

$b
[1] 0.7582138