Combinations for each column in list of dataframes in R

Viewed 187

I have a list of dataframes, each with two columns. Columns have the same class and name in each dataframe and generally only differ in number of rows, e.g.,

lst = list(data.frame(n=1:3,
                          l=letters[seq(1:3)]),
            data.frame(n=1:5,
                          l=letters[seq(1:5)]),
            data.frame(n=1:8,
                          l=letters[seq(1:8)]))

I'm trying to create a new list of dataframes containing all possible pairwise combinations of values within each column (not between columns), but I haven't been able to get it right.

This code gives me the desired output for a single column, but I haven't been able to figure out how to apply it to all columns in all dataframes in the list using either lapply or for loops, though I'm sure there must be something I'm missing.

library(gtools)
library(tidyverse)

as.data.frame(combinations(nrow(lst[[1]]), 2, v=lst[[1]]$n, set=T, repeats.allowed=F)) %>%
  transmute(x = paste(V1, V2, sep=","))

I'd really appreciate any tips!

2 Answers

We can use map to loop over the list and then with summarise_all apply the combinations

library(dplyr)
library(purrr)
library(tidyr)
library(gtools)
library(stringr)
map(lst, ~ .x %>% 
      mutate_if(is.factor, as.character) %>%
      summarise_all(~ list(combinations(n(),  2, v = .,
          set  = TRUE,  repeats.allowed = FALSE) %>% 
          as_tibble %>%
          transmute(x = reduce(., str_c,  sep=", ")))) %>% 
      unnest(everything(), names_repair = 'unique') %>% 
      rename_all( ~ str_remove(., "\\.+")))
#[[1]]
# A tibble: 3 x 2
#  x1    x2   
#  <chr> <chr>
#1 1, 2  a, b 
#2 1, 3  a, c 
#3 2, 3  b, c 

#[[2]]
# A tibble: 10 x 2
#   x1    x2   
#   <chr> <chr>
# 1 1, 2  a, b 
# 2 1, 3  a, c 
# 3 1, 4  a, d 
# 4 1, 5  a, e 
# 5 2, 3  b, c 
# 6 2, 4  b, d 
# 7 2, 5  b, e 
# 8 3, 4  c, d 
# 9 3, 5  c, e 
#10 4, 5  d, e 

#[[3]]
# A tibble: 28 x 2
#   x1    x2   
#   <chr> <chr>
# 1 1, 2  a, b 
# 2 1, 3  a, c 
# 3 1, 4  a, d 
# 4 1, 5  a, e 
# 5 1, 6  a, f 
# 6 1, 7  a, g 
# 7 1, 8  a, h 
# 8 2, 3  b, c 
# 9 2, 4  b, d 
#10 2, 5  b, e 
# … with 18 more rows

Or another option is with crossing and then remove the mirror rows and the rows where two columns are same with filter

map(lst, ~ 
       map(.x %>%
               mutate_if(is.factor, as.character),
               ~ crossing(x1 = .x, x2 = .x) %>% 
                   filter(x1 != x2)  %>% 
                   filter(!duplicated(cbind(pmin(x1, x2), pmax(x1, x2)))) %>%
                   transmute(x = reduce(., str_c, sep=", "))) %>% 
               bind_cols %>% 
               rename_all(~ str_remove(., "\\.+")))

try using expand.grid(). Imagine that a, b and c are lists. If you use the function, it will create all the possible combinations

Related