1

Given the following data frame:

data_frame = data.frame(food = c("pizza", "tacos", "nachos"), drinks = c("water", "coffee", "pop"))

 data_frame
    food drinks
1  pizza  water
2  tacos coffee
3 nachos    pop

Is there a way to list ALL combinations of these factors?

For example:

food_combinations = c("none", pizza", "tacos", "nachos", "pizza & tacos", "pizza & nachos", "tacos & nachos", "pizza & tacos & nachos")

drink_combinations = c("none", coffee", "pop", "water", "coffee & pop", "coffee & water", "pop & water", "coffee & pop & water")

Thanks!

stats_noob
  • 5,401
  • 4
  • 27
  • 83
  • Does this answer your question? [Generate list of all possible combinations of elements of vector](https://stackoverflow.com/questions/18705153/generate-list-of-all-possible-combinations-of-elements-of-vector) – NelsonGon Dec 28 '21 at 20:24
  • 1
    @NelsonGon that's not a good duplicate--that one is a question that could be answered with `expand.grid`, this is different. Better ones would be https://stackoverflow.com/q/66676933/5325862 or https://stackoverflow.com/q/29387475/5325862 – camille Dec 28 '21 at 21:57

2 Answers2

4

We can use combn to do - loop over the columns with lapply, then do a nested loop over the sequence of the elements, apply combn and paste

lst1 <- lapply(data_frame, \(x) c("none", unlist(lapply(seq_len(length(x)),
        \(i) combn(x, i, FUN = paste, collapse = " & ")))))

-output

> lst1
$food
[1] "none"                   "pizza"                  "tacos"                  "nachos"                 "pizza & tacos"          "pizza & nachos"        
[7] "tacos & nachos"         "pizza & tacos & nachos"

$drinks
[1] "none"                 "water"                "coffee"               "pop"                  "water & coffee"       "water & pop"          "coffee & pop"        
[8] "water & coffee & pop"
akrun
  • 874,273
  • 37
  • 540
  • 662
  • @ Akrun : thank you so much for your answer! I tried running your answer and got the following error – stats_noob Dec 28 '21 at 20:29
  • lst1 <- lapply(data_frame, \(x) c("none", unlist(lapply(seq_len(length(x)), \(i) combn(x, i, FUN = paste, collapse = " & "))))) – stats_noob Dec 28 '21 at 20:29
  • Error: unexpected input in "lst1 <- lapply(data_frame, \" > \(i) combn(x, i, FUN = paste, collapse = " & "))))) Error: unexpected input in " – stats_noob Dec 28 '21 at 20:29
  • Can you please tell me what I am doing wrong? Thanks! – stats_noob Dec 28 '21 at 20:29
  • @Noob If you have a `R version` which is `< 4.0`, then change the `\(x)` `(\i)` to `function(x)` and `function(i)` as this is a new operator introduced for compacting the lambda call – akrun Dec 28 '21 at 20:31
  • 1
    For clarity, it's an R version `< 4.1`. The `\(x)` notation was added in R-4.1.0 (see https://cran.r-project.org/doc/manuals/r-release/NEWS.html). – r2evans Dec 28 '21 at 20:47
  • everything works now! lst1 <- lapply(data_frame, function(x) c("none", unlist(lapply(seq_len(length(x)), function(i) combn(x, i, FUN = paste, collapse = " & "))))) – stats_noob Dec 28 '21 at 21:01
  • also, I think this allows you to store each part of the list in a separate object: a = lst1$food – stats_noob Dec 28 '21 at 21:01
2

-Edit: adopted the response by @akrun as it is more concise and doesn't rely on any packages

Here is an alternative way, in the form of a function. Parameter m allows you to choose the maximum elements in each phrase.

df <- data.frame(
  food = c("pizza", "tacos", "nachos"),
  drinks = c("water", "coffee", "pop")
)

comb1 <- function(vector, m = length(vector)) {
    if (m >= length(vector)) {
      data <- unlist(lapply(seq_len(length(df$food)),
                            \(i) combn(df$food, i, paste, collapse = ' & ')))
    }
    else {
      data <- unlist(lapply(seq_len(m),
                            \(i) combn(df$food, i, paste, collapse = ' & ')))
    }
    return(data)
  }

Which renders

> comb1(df$food)

[1] "none"                   "nachos"                 "pizza"                 
[4] "tacos"                  "nachos & pizza"         "nachos & tacos"        
[7] "pizza & tacos"          "nachos & pizza & tacos"

> comb1(df$food,2)

[1] "none"           "nachos"         "pizza"          "tacos"          "nachos & pizza"
[6] "nachos & tacos" "pizza & tacos" 

For a list over the dataframe then just a lapply

> lapply(df, comb1)

$food
[1] "none"                   "nachos"                 "pizza"                 
[4] "tacos"                  "nachos & pizza"         "nachos & tacos"        
[7] "pizza & tacos"          "nachos & pizza & tacos"

$drinks
[1] "none"                 "coffee"               "pop"                 
[4] "water"                "coffee & pop"         "coffee & water"      
[7] "pop & water"          "coffee & pop & water"

> lapply(df, \(x) comb1(x,2))

$food
[1] "none"           "nachos"         "pizza"          "tacos"          "nachos & pizza"
[6] "nachos & tacos" "pizza & tacos" 

$drinks
[1] "none"           "coffee"         "pop"            "water"          "coffee & pop"  
[6] "coffee & water" "pop & water"  
Martin
  • 307
  • 1
  • 10