Generate all unique combinations from a vector with repeating elements
Asked Answered
D

3

8

This questions was asked previously but only for vectors with non-repeating elements. I was not able to find an easy solution to get all combinations from a vector with repeating elements. To illustrate I listed an example below.

x <- c('red', 'blue', 'green', 'red', 'green', 'red')

Vector x has 3 repeating elements for 'red' and 2 for 'green'. The expected outcome for all unique combinations would be like this.

# unique combinations with one element
'red'
'blue'
'green'
# unique combination with two elements
'red', 'blue' # same as 'blue','red'
'red', 'green' 
'red', 'red'
'blue', 'green'
'green', 'green'
# unique combination with three elements
'red', 'blue', 'green'
'red', 'red', 'blue'
'red', 'red', 'green'
'red', 'red', 'red' # This is valid because there are three 'red's
'green', 'green', 'red'
'green', 'green', 'blue'
# more unique combinations with four, five, and six elements
Doughman answered 14/3, 2019 at 13:38 Comment(0)
S
8

Use combn() with lapply() should do the trick.

x <- c('red', 'blue', 'green', 'red', 'green', 'red')

lapply(1:3, function(y) combn(x, y))

# [[1]]
     # [,1]  [,2]   [,3]    [,4]  [,5]    [,6] 
# [1,] "red" "blue" "green" "red" "green" "red"

# [[2]]
     # [,1]   [,2]    [,3]  [,4]    [,5]  [,6]    ...
# [1,] "red"  "red"   "red" "red"   "red" "blue"  ...
# [2,] "blue" "green" "red" "green" "red" "green" ...

# [[3]]
     # [,1]    [,2]   [,3]    [,4]   [,5]    [,6]    ...
# [1,] "red"   "red"  "red"   "red"  "red"   "red"   ...
# [2,] "blue"  "blue" "blue"  "blue" "green" "green" ...
# [3,] "green" "red"  "green" "red"  "red"   "green" ...

All unique combinations

lapply(cc, function(y)
  y[,!duplicated(apply(y, 2, paste, collapse="."))]
)

[[1]]
[1] "red"   "blue"  "green"

[[2]]
     [,1]   [,2]    [,3]  [,4]    [,5]   [,6]    [,7]   
[1,] "red"  "red"   "red" "blue"  "blue" "green" "green"
[2,] "blue" "green" "red" "green" "red"  "red"   "green"

[[3]]
     [,1]    [,2]   [,3]    [,4]    [,5]    [,6]  [,7]    ...
[1,] "red"   "red"  "red"   "red"   "red"   "red" "blue"  ...
[2,] "blue"  "blue" "green" "green" "red"   "red" "green" ...
[3,] "green" "red"  "red"   "green" "green" "red" "red"   ...

Although strictly speaking those aren't all unique combinations, as some of them are permutations of each other.

Properly unique combinations

lapply(cc, function(y)
  y[,!duplicated(apply(y, 2, function(z) paste(sort(z), collapse=".")))]
)

# [[1]]
# [1] "red"   "blue"  "green"

# [[2]]
     # [,1]   [,2]    [,3]  [,4]    [,5]   
# [1,] "red"  "red"   "red" "blue"  "green"
# [2,] "blue" "green" "red" "green" "green"

# [[3]]
     # [,1]    [,2]   [,3]    [,4]    [,5]  [,6]   
# [1,] "red"   "red"  "red"   "red"   "red" "blue" 
# [2,] "blue"  "blue" "green" "green" "red" "green"
# [3,] "green" "red"  "red"   "green" "red" "green"
Sauropod answered 14/3, 2019 at 13:46 Comment(4)
Or lapply(1:3, function(k) matrix(x[combn(i, k)], nrow=k)). The OP also wants the combinations of 1 and of 2. Then apply unique.Eadwina
@RuiBarradas: Ah, I see. Appears like I don't have do the index trick after all.Sauropod
This solution with generate all possible combinations but not all unique combinations.Doughman
@Jian: Think I've got it now.Sauropod
D
4
library(DescTools)
x <- c('red', 'blue', 'green', 'red', 'green', 'red')

allSets <- lapply(1:length(x), function(i){
      unique(t(apply(CombSet(x,i,repl = F),1,sort)))
    })


#[1]]
#[,1]  [,2]   [,3]    [,4]  [,5]    [,6] 
#[1,] "red" "blue" "green" "red" "green" "red"

##[[2]]
#[,1]    [,2]   
#[1,] "blue"  "red"  
#[2,] "green" "red"  
#[3,] "red"   "red"  
#[4,] "blue"  "green"
#[5,] "green" "green"

#[[3]]
#[,1]    [,2]    [,3]   
#[1,] "blue"  "green" "red"  
#[2,] "blue"  "red"   "red"  
#[3,] "green" "red"   "red"  
#[4,] "green" "green" "red"  
#[5,] "red"   "red"   "red"  
#[6,] "blue"  "green" "green"

#[[4]]
#[,1]    [,2]    [,3]    [,4] 
#[1,] "blue"  "green" "red"   "red"
#[2,] "blue"  "green" "green" "red"
#[3,] "blue"  "red"   "red"   "red"
#[4,] "green" "green" "red"   "red"
#[5,] "green" "red"   "red"   "red"

#[[5]]
#[,1]    [,2]    [,3]    [,4]  [,5] 
#[1,] "blue"  "green" "green" "red" "red"
#[2,] "blue"  "green" "red"   "red" "red"
#[3,] "green" "green" "red"   "red" "red"

#[[6]]
#[,1]   [,2]    [,3]    [,4]  [,5]  [,6] 
#[1,] "blue" "green" "green" "red" "red" "red"
Dorseydorsiferous answered 14/3, 2019 at 14:0 Comment(4)
OP said " more unique combinations with four, five, and six elements" at the end of the question. I supposed that he wants all six. Am I understanding wrong the question?Dorseydorsiferous
Actually I think you are right. My bad. Did not see that last sentence. :)Plenty
Thanks, this solution works except the one component [[1]] case.Doughman
Can I save this is anyway as a dataframe or concatenate each of these into one column?Peddle
S
4
library(arrangements)

combinations(c("red", "blue", "green"), k = 2, freq = c(3, 1, 2))
#      [,1]    [,2]   
# [1,] "red"   "red"  
# [2,] "red"   "blue" 
# [3,] "red"   "green"
# [4,] "blue"  "green"
# [5,] "green" "green"

combinations(c("red", "blue", "green"), k = 3, freq = c(3, 1, 2))
#      [,1]   [,2]    [,3]   
# [1,] "red"  "red"   "red"  
# [2,] "red"  "red"   "blue" 
# [3,] "red"  "red"   "green"
# [4,] "red"  "blue"  "green"
# [5,] "red"  "green" "green"
# [6,] "blue" "green" "green"

If you don't want to manually enter the frequencies:

x <- c('red', 'blue', 'green', 'red', 'green', 'red')
tx <- table(x)
combinations(names(tx), k = 2, freq = tx)
#       [,1]    [,2]   
# [1,] "blue"  "green"
# [2,] "blue"  "red"  
# [3,] "green" "green"
# [4,] "green" "red"  
# [5,] "red"   "red" 

Or using RcppAlgos:

library(RcppAlgos)
comboGeneral(names(tx), m=2, freqs = tx)
#       [,1]    [,2]   
# [1,] "blue"  "green"
# [2,] "blue"  "red"  
# [3,] "green" "green"
# [4,] "green" "red"  
# [5,] "red"   "red" 
Succory answered 14/3, 2019 at 14:2 Comment(1)
Thanks, this works if iterating m 1:6 in the comboGeneral. Appreciate your answer.Doughman

© 2022 - 2024 — McMap. All rights reserved.