1

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)

enter image description here Here line 9 shows the solution that maximizes "obj".

2 Answers 2

2

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(.))
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, akrun! But how can we make it general (i.e. for brad_1, brand_2, ..., brand_n)?
@Ketty Sure, we can get all the elements loaded as mget(ls(pattern = '^brand_\\d+$')) %>% bind_cols %>% complete(!!! rlang::syms(names(.))) %>% mutate(all_obj = rowSums(.))
1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.