Combinations of multiple vectors in R
Asked Answered
O

3

9

I'm not sure if permutations is the correct word for this. I want to given a set of n vectors (i.e. [1,2],[3,4] and [2,3]) permute them all and get an output of

[1,3,2],[1,3,3],[1,4,2],[1,4,3],[2,3,2] etc.

Is there an operation in R that will do this?

Overdrive answered 12/11, 2011 at 22:37 Comment(1)
I believe you are describing combinations. Permutations are similar to combinations, but the order of elements matter.Surroundings
B
25

This is a useful case for storing the vectors in a list and using do.call() to arrange for an appropriate function call for you. expand.grid() is the standard function you want. But so you don't have to type out or name individual vectors, try:

> l <- list(a = 1:2, b = 3:4, c = 2:3)
> do.call(expand.grid, l)
  a b c
1 1 3 2
2 2 3 2
3 1 4 2
4 2 4 2
5 1 3 3
6 2 3 3
7 1 4 3
8 2 4 3

However, for all my cleverness, it turns out that expand.grid() accepts a list:

> expand.grid(l)
  a b c
1 1 3 2
2 2 3 2
3 1 4 2
4 2 4 2
5 1 3 3
6 2 3 3
7 1 4 3
8 2 4 3
Baltimore answered 12/11, 2011 at 22:57 Comment(1)
@Surroundings How embarrassing! Trying to be too clever does have its advantages it would appear. I don't deserve the Accept here though. I shall make amends with some upvotes.Baltimore
S
14

This is what expand.grid does.

Quoting from the help page: Create a data frame from all combinations of the supplied vectors or factors. The result is a data.frame with a row for each combination.

expand.grid(
    c(1, 2),
    c(3, 4),
    c(2, 3)
)

  Var1 Var2 Var3
1    1    3    2
2    2    3    2
3    1    4    2
4    2    4    2
5    1    3    3
6    2    3    3
7    1    4    3
8    2    4    3
Surroundings answered 12/11, 2011 at 22:44 Comment(2)
Hi, I am trying this approach, but am getting the message: "Error in rep.int(rep.int(seq_len(nx), rep.int(rep.fac, nx)), orep) : vector is too large" Are there any alternative ways to do this?Inscribe
@Inscribe How large are your vectors?Blaineblainey
G
0

As an alternative to expand.grid() you could use rep() to produce the desired combination. Consider the following simplified example using the original data from this question:

a <- c(1,2)
b <- c(3,4)
c <- c(2,3)

To get the expand.grid()-like effect, use rep() with a times= argument equal to the product of the length of the other vectors (or 4). The middle vector would use a nested rep() with products of vector length to either side (or 2 and 2). The end vector is like the first but with each= argument in order to pattern correctly. This is trivial to calculate when each vector is length of 2. Example:

#tibble of all combinations of a, b and c
tibble::tibble(
var1 = rep(a, times = 4),
var2 = rep(rep(b, each= 2), times = 2), #nested rep()
var3 = rep(c, each= 4)
)

For an unknown number of input vectors (or unknown vector lengths), we can get all combinations with rep() in a function like this:

#Produces a tibble of all combinations of input vectors
expand_tibble <- function(...){ 
  x <- list(...) #all input vectors stored here
  l <- lapply(x,length)|> unlist() #vector showing length of each input vector
  t <- length(l) #total input vector count
  r <-list() #empty list
  for(i in 1:t){
    if(i==1){ #first input vector
      first <-l[2:length(l)] |> prod()
       r[[i]]<-rep(x[[i]], each = first)
    }else{   #last input vector
    if(i==t){
    last  <- l[1:t-1] |> prod()
    r[[i]]<-rep(x[[i]], last)
    }else{   #all middle input vectors
      m1 <- l[1:(i-1)] |> prod()
      m2 <- l[(i+1):t] |> prod()  
    r[[i]] <- rep(rep(x[[i]], each=m1),m2)
        }
    }
    names(r)[i]<-paste0("var",i)
    }
  tibble::as_tibble(r)
}  

output:

expand_tibble(a,b,c)
 var1  var2  var3
  <dbl> <dbl> <dbl>
1     1     3     2
2     1     3     3
3     1     4     2
4     1     4     3
5     2     3     2
6     2     3     3
7     2     4     2
8     2     4     3
Gratiana answered 16/8, 2022 at 19:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.