2

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?

Joseph Wood
  • 5,839
  • 2
  • 25
  • 55
Golden Jiang
  • 213
  • 3
  • 11

2 Answers2

2

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)
dmca
  • 633
  • 1
  • 8
  • 16
  • Magic! But can I have more explanation on the code? I'm not used to pipes yet. – Golden Jiang Nov 09 '18 at 11:55
  • 1. The first map is taking each matrix your list and passing it into `t()` to make each element n x m, rather than m x n; 2. The second map is passing each matrix element into a lambda function that splits the matrix into one list per row; 3. the `unlist()` flattens this list-of-list into a list of vectors. – dmca Nov 10 '18 at 00:22
  • What is the `~` before the `split`? Once I take it out, it shows `Error in 1:nrow(.) : argument of length 0`. Also, is `map` kind of like `lapply`? – Golden Jiang Nov 10 '18 at 21:27
  • This explains the `~`: https://stackoverflow.com/questions/14976331/use-of-tilde-in-r-programming-language – dmca Nov 11 '18 at 22:02
  • This provides context on `purrr::map()`: https://stackoverflow.com/questions/45101045/why-use-purrrmap-instead-of-lapply – dmca Nov 11 '18 at 22:03
2

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"
Joseph Wood
  • 5,839
  • 2
  • 25
  • 55