Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create all combinations from a nested list while preserving the structure using R?

Tags:

list

r

recursion

Given a nested list, how to create all possible lists from its elements, while preserving the structure of the nested list?

Nested list:

l = list(
    a = list(
        b = 1:2
    ),
    c = list(
        d = list(
            e = 3:4,
            f = 5:6
        )
    ),
    g = 7
)

Desired output: all possible combinations of the elements of l, while preserving the structure, e.g.:

# One possible output:
list(
    a = list(
        b = 1
    ),
    c = list(
        d = list(
            e = 3,
            f = 5
        )
    ),
    g = 7
)

# Another possible output:
list(
    a = list(
        b = 1
    ),
    c = list(
        d = list(
            e = 4,
            f = 5
        )
    ),
    g = 7
)

My approach so far is to:

  1. flatten the list (e.g., as discussed in this answer)
  2. expand.grid() and get a matrix where each row represents a unique combination
  3. parse every row of the resulting matrix and reconstruct the structure from the names() using regular expressions

I am looking for a less cumbersome approach because I have no guarantee that the names of the list elements will not change.

like image 721
Mihai Avatar asked Aug 11 '19 07:08

Mihai


People also ask

How does nested list store values?

Add items to a Nested list. To add new values to the end of the nested list, use append() method. When you want to insert an item at a specific position in a nested list, use insert() method. You can merge one list into another by using extend() method.

What is nested Listt?

A nested list is simply a list that occurs as an element of another list (which may of course itself be an element of another list, etc.). Common reasons nested lists arise are: They're matrices (a list of rows, where each row is itself a list, or a list of columns where each column is itself a list).


2 Answers

The relist function from utils seems to be designed for this task:

rl <- as.relistable(l)
r <- expand.grid(data.frame(rl), KEEP.OUT.ATTRS = F)
> head(r, 5)
   b c.d.e c.d.f g
1  1     3     5 7
2  2     3     5 7
3  1     4     5 7
4  2     4     5 7
5  1     3     6 7

It saves the structure of the list (skeleton). This means one can now manipulate the data within the nested list and re-assign it into the structure (flesh). Here with the first row of the expanded matrix.

r <- rep(unname(unlist(r[1,])),each = 2)
l2 <- relist(r, skeleton = rl)
> l2
$a
$a$b
[1] 1 1


$c
$c$d
$c$d$e
[1] 3 3

$c$d$f
[1] 5 5



$g
[1] 7

attr(,"class")
[1] "relistable" "list"  

Note that since the structure stays the same, I need to supply the same amount of elements as in the original list. This is why used rep to repeat the element twice. One could also fill it with NA, I guess.

For every possible combination iterate through r (expanded):

lapply(1:nrow(r), function(x) 
          relist(rep(unname(unlist(r[x,])),each = 2), skeleton = rl))
like image 50
Ben Nutzer Avatar answered Sep 28 '22 05:09

Ben Nutzer


Combining Ben Nutzer's brilliant answer and Joris Chau's brilliant comment, the answer will become a one-liner:

apply(expand.grid(data.frame(l)), 1L, relist, skeleton = rapply(l, head, n = 1L, how = "list")) 

It creates a list of lists with as many elements as rows returned by expand.grid(). The result is better visualised by the output of str():

str(apply(expand.grid(data.frame(l)), 1L, relist, skeleton = rapply(l, head, n = 1L, how = "list")))
List of 16
 $ :List of 3
  ..$ a:List of 1
  .. ..$ b: num 1
  ..$ c:List of 1
  .. ..$ d:List of 2
  .. .. ..$ e: num 3
  .. .. ..$ f: num 5
  ..$ g: num 7
 $ :List of 3
  ..$ a:List of 1
  .. ..$ b: num 2
  ..$ c:List of 1
  .. ..$ d:List of 2
  .. .. ..$ e: num 3
  .. .. ..$ f: num 5
  ..$ g: num 7
...
...
...
 $ :List of 3
  ..$ a:List of 1
  .. ..$ b: num 2
  ..$ c:List of 1
  .. ..$ d:List of 2
  .. .. ..$ e: num 4
  .. .. ..$ f: num 6
  ..$ g: num 7
like image 45
2 revs, 2 users 69% Avatar answered Sep 28 '22 06:09

2 revs, 2 users 69%