How to create a new list that contain each individual matrix column from an old list in R?

Viewed 122

I try to create a new list that contain each individual matrix column from an old list. For example:

var_names <- c('a', 'b', 'c', 'd')
var_combi <- list()
for(i in 1:length(var_names)) {var_combi[[i]] <- combn(var_names[1:length(var_names)],i)}
c(var_combi)

Then I got a list object like below. Each listed item is a matrix:

[[1]]
     [,1] [,2] [,3] [,4]
[1,] "a"  "b"  "c"  "d" 

[[2]]
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,] "a"  "a"  "a"  "b"  "b"  "c" 
[2,] "b"  "c"  "d"  "c"  "d"  "d" 

[[3]]
     [,1] [,2] [,3] [,4]
[1,] "a"  "a"  "a"  "b" 
[2,] "b"  "b"  "c"  "c" 
[3,] "c"  "d"  "d"  "d" 

[[4]]
     [,1]
[1,] "a" 
[2,] "b" 
[3,] "c" 
[4,] "d" 

I want to create a new list new_list such as below, e.g.

[[1]]
[1] "a"

[[2]] 
[1] "b"

[[3]] 
[1] "c" 

[[4]]
[1] "d" 

[[5]]
[1]  "a" "b"

[[6]]
[1]  "a" "c"

So that I can loop over them or use lapply to select those variables from my dataset mydata using mydata[names(mydata) %in% new_list[[i]]].

Now I'm struggle to create new_list and couldn't find a proper solution for it (I tried unlist, c and append). If I have to use for loop, how do I loop over a list and listed matrix columns here?

2 Answers

If you don't mind using the functional approach instead of looping, this should do the trick:

library(dplyr)
library(purrr)

c(var_combi) %>%
  map(t) %>%
  map(~ split(., 1:nrow(.))) %>%
  unlist(recursive = F)

Here is a straightforward approach using base R:

unlist(lapply(seq_along(var_names), function(x) {
    combn(var_names, x, simplify = FALSE)
}), recursive = FALSE)

[[1]]
[1] "a"

[[2]]
[1] "b"

[[3]]
[1] "c"

[[4]]
[1] "d"

[[5]]
[1] "a" "b"

[[6]]
[1] "a" "c"
.
.    ### 9 more elements
.

This is a minor modification of the OP's code. We use here lapply instead of a for loop and we also set simplify = FALSE in combn (the default is simplify = TRUE). This argument, if FALSE, will leave the result in the requested list format. You will also note that we use unlist with rescursive = FALSE which stops unlisting after the first level.

If you really want conciseness, we can use the powerset function from the rje library. We must keep in my that we need to remove the first element which is equivalent to the empty set:

rje::powerSet(letters[1:4])[-1]

[[1]]
[1] "a"

[[2]]
[1] "b"

[[3]]
[1] "a" "b"

[[4]]
[1] "c"

[[5]]
[1] "a" "c"

[[6]]
[1] "b" "c"

[[7]]
[1] "a" "b" "c"

[[8]]
[1] "d"

[[9]]
[1] "a" "d"

[[10]]
[1] "b" "d"

[[11]]
[1] "a" "b" "d"

[[12]]
[1] "c" "d"

[[13]]
[1] "a" "c" "d"

[[14]]
[1] "b" "c" "d"

[[15]]
[1] "a" "b" "c" "d"
Related