Reputation: 851
I am trying to find the combination of columns "a" and "b" in table "brand_1" and "brand_2" that maximize my objective function "obj." How can I accomplish this without writing 4 for-loops?
Here is a simple code to illustrate my question. I also need to generalize it so that it works for any number of tables (not just "brand_1" and "brand_2") with each table will always have two columns ("a" and "b"). Thanks so much!
brand_1 <- data.frame(a = c(1, 3), b = c(5, 2))
brand_2 <- data.frame(a = c(5, 4), b = c(2, 1))
num_row <- nrow(brand_1) # where nrow(brand_1) = nrow(brand_2)
all_a1 <- vector()
all_b1 <- vector()
all_a2 <- vector()
all_b2 <- vector()
all_obj <- vector()
for (a1 in 1:num_row) {
for (b1 in 1:num_row) {
for (a2 in 1:num_row) {
for (b2 in 1:num_row) {
obj <- brand_1[a1, "a"] + brand_1[b1, "b"] + brand_2[a2, "a"] + brand_2[b2, "b"]
all_a1 <- c(all_a1, brand_1[a1, "a"])
all_b1 <- c(all_b1, brand_1[b1, "b"])
all_a2 <- c(all_a2, brand_2[a2, "a"])
all_b2 <- c(all_b2, brand_2[b2, "b"])
all_obj <- c(all_obj, obj)
}
}
}
}
out <- data.frame(all_a1, all_b1, all_a2, all_b2, all_obj)
Here line 9 shows the solution that maximizes "obj".
Upvotes: 1
Views: 56
Reputation: 389265
For a general solution with any number of dataframes we can use ls
to get all the objects with "brand"
in it, get them in list using mget
, rename the column names based on there object name and use crossing
to generate their all possible combinations. Finally, you can use rowSums
to sum the rows.
library(tidyverse)
mget(ls(pattern = 'brand')) %>%
imap_dfc(~setNames(., paste0(names(.x), sub('.*_', '', .y)))) %>%
do.call(crossing, .) %>%
mutate(all_obj = rowSums(.))
# a1 b1 a2 b2 all_obj
# <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 1 2 4 1 8
# 2 1 2 4 2 9
# 3 1 2 5 1 9
# 4 1 2 5 2 10
# 5 1 5 4 1 11
# 6 1 5 4 2 12
# 7 1 5 5 1 12
# 8 1 5 5 2 13
# 9 3 2 4 1 10
#10 3 2 4 2 11
#11 3 2 5 1 11
#12 3 2 5 2 12
#13 3 5 4 1 13
#14 3 5 4 2 14
#15 3 5 5 1 14
#16 3 5 5 2 15
Upvotes: 1
Reputation: 887881
We can use expand.grid
out <- expand.grid(c(brand_1, brand_2))
names(out) <- make.unique(names(out))
out$all_obj <- rowSums(out)
-output
out
# a b a.1 b.1 all_obj
#1 1 5 5 2 13
#2 3 5 5 2 15
#3 1 2 5 2 10
#4 3 2 5 2 12
#5 1 5 4 2 12
#6 3 5 4 2 14
#7 1 2 4 2 9
#8 3 2 4 2 11
#9 1 5 5 1 12
#10 3 5 5 1 14
#11 1 2 5 1 9
#12 3 2 5 1 11
#13 1 5 4 1 11
#14 3 5 4 1 13
#15 1 2 4 1 8
#16 3 2 4 1 10
Or with complete
library(dplyr)
library(tidyr)
bind_cols(brand_1, brand_2) %>%
complete(!!! rlang::syms(names(.))) %>%
mutate(all_obj = rowSums(.))
Upvotes: 2