Find all combinations between two vectors of different length

Viewed 399

Let's suppose I have two vectors of different length x_1 and y_1 as follows:

x_1 = seq(0,49,2)
y_1 = seq(-90,-51,2)

Now I want to find all the possible combinations, with size = 5, of these two vectors and therefore I do:

library(tidyr)
x_all = data.frame(t(rbind(combn(x_1, 5))))
y_all = data.frame(t(rbind(combn(y_1, 5))))

Now I want to keep only the combinations with values progressing by a step of 2:

x_all = x_all[x_all$X2 - x_all$X1 == 2 &
                x_all$X3 - x_all$X2 == 2 &
                x_all$X4 - x_all$X3 == 2 &
                x_all$X5 - x_all$X4 == 2, ]

y_all = y_all[y_all$X2 - y_all$X1 == 2 &
                y_all$X3 - y_all$X2 == 2 &
                y_all$X4 - y_all$X3 == 2 &
                y_all$X5 - y_all$X4 == 2, ]

And now my question:

How can I merge x_all and y_all so that I get a unique data.frame with all the possible combinations between these two sets?

Any suggestions?

Here what I tried without success:

# Assign an index to each set
x_all$index = c(1:nrow(x_all))
y_all$index = c(1:nrow(y_all))

# Merge the sets
x_y_all = merge(x_all, y_all, by = 'index', all = TRUE)

The output should look something like this:

X1.x X2.x X3.x X4.x X5.x X1.y X2.y X3.y X4.y X5.y 
 0    2    4    6    8   -90  -88  -86  -84  -82
 0    2    4    6    8   -88  -86  -84  -82  -80
 0    2    4    6    8   -86  -84  -82  -80  -78
....
 2    4    6    8   10   -90  -88  -86  -84  -82
 2    4    6    8   10   -88  -86  -84  -82  -80
 2    4    6    8   10   -86  -84  -82  -80  -78
....
4 Answers

A small change to your suggestion solves the problem:

#Assign same, constant index so the merge function will fill
x_all$index <- 1
y_all$index <- 1

#Merge to get all information
x_y_all <- merge(x_all, y_all, by = "index")

#Delete the index
x_y_all$index <- NULL

Here's a simpler way (also easier on memory than your combn approach) of getting your desired result -

library(dplyr)

x_1 = seq(0, 49, 2)
y_1 = seq(-90, -51, 2)

x_all <- sapply(x_1, function(x) {
  seq(x, by = 2, length.out = 5)
}) %>% 
  t() %>% 
  as_tibble() %>% 
  mutate(cj = 1)

y_all <- sapply(y_1, function(x) {
  seq(x, by = 2, length.out = 5)
}) %>% 
  t() %>% 
  as_tibble() %>% 
  mutate(cj = 1)

inner_join(x_all, y_all, by = "cj") %>% 
  select(-cj)

# A tibble: 500 x 10
    V1.x  V2.x  V3.x  V4.x  V5.x  V1.y  V2.y  V3.y  V4.y  V5.y
   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
 1     0     2     4     6     8   -90   -88   -86   -84   -82
 2     0     2     4     6     8   -88   -86   -84   -82   -80
 3     0     2     4     6     8   -86   -84   -82   -80   -78
 4     0     2     4     6     8   -84   -82   -80   -78   -76
 5     0     2     4     6     8   -82   -80   -78   -76   -74
 6     0     2     4     6     8   -80   -78   -76   -74   -72
 7     0     2     4     6     8   -78   -76   -74   -72   -70
 8     0     2     4     6     8   -76   -74   -72   -70   -68
 9     0     2     4     6     8   -74   -72   -70   -68   -66
10     0     2     4     6     8   -72   -70   -68   -66   -64
# ... with 490 more rows

Another possible base R approach by using indexing to create your x_all and y_all combinations (without creating many combinations in combn and then subsetting) and then cross join your combinations:

x_1 = seq(0,49,2)
y_1 = seq(-90,-51,2)

#creating combinations
x_all <- do.call(rbind, lapply(head(seq_along(x_1), -4L), function(n) x_1[n + 0L:4L]))
y_all <- do.call(rbind, lapply(head(seq_along(y_1), -4L), function(n) y_1[n + 0L:4L]))
#or also 
#x_nc <- length(x_1)-4L
#x_all <- matrix(x_1[t(embed(seq_along(x_1), x_nc)[, x_nc:1L])], ncol=5L)

#cross join
cbind(
    x_all[rep(seq_len(nrow(x_all)), each=nrow(y_all)),],
    y_all[rep(seq_len(nrow(y_all)), times=nrow(x_all)),]
)

Here my (not elegant) solution:

# create empty list
    x_all_lst = list()

# put into list the `x_all` data.frame n times based on the number of y_all combinations (here 16)
    for (i in 1:nrow(y_all)) {
      x_all_lst[[i]] = x_all
    }

# merge list
    x_all = do.call(rbind, x_all_lst)

# order list by column
    x_all = x_all[with(x_all, order(X1)), ]

# bind x_all and y_all columns
    x_y_all = cbind(x_all, y_all)

# remove row.names
    row.names(x_y_all) = NULL
Related