0

With the data frame below of Locations, Days, and Quantities, I'm searching for a solution to create combinations of quantities by Location across each Day. In production, these combinations may grow pretty large, so a data.table or plyr approach would be appreciated.

library(gtools)    
dat <- data.frame(Loc = c(51,51,51,51,51), Day = c("Mon","Mon","Tue","Tue","Wed"),
Qty = c(1,2,3,4,5))

The output for this example should be:

  Loc Day  Qty
1  51 Mon   1
2  51 Tue   3
3  51 Wed   5

4  51 Mon   1
5  51 Tue   4
6  51 Wed   5

7  51 Mon   2
8  51 Tue   3
9  51 Wed   5

10  51 Mon  2
11  51 Tue  4
12  51 Wed  5

I've tried a few nested lapply's which gets me close, but then I'm not sure how to take it to the next step and use the combn() function within each store.

lapply(split(dat, dat$Loc), function(x) {
      lapply(split(x, x$Day), function(y) {
          y$Qty
    })                
})

I'm able to get the correct combinations if each Store > Day group was in it's own list, but am struggling how to get there from a data frame using a split-apply-combine method.

loc51_mon <- c(1,2)
loc51_tue <- c(3,4)
loc51_wed <- c(5)

unlist(lapply(loc51_mon, function(x) {
    lapply(loc51_tue, function(y) {
         lapply(loc51_wed, function(z) {
              combn(c(x,y,z), 3)
         })
    })
}), recursive = FALSE)

[[1]]
[[1]][[1]]
     [,1]
[1,]    1
[2,]    3
[3,]    5

[[2]]
[[2]][[1]]
     [,1]
[1,]    1
[2,]    4
[3,]    5

[[3]]
[[3]][[1]]
     [,1]
[1,]    2
[2,]    3
[3,]    5

[[4]]
[[4]][[1]]
     [,1]
[1,]    2
[2,]    4
[3,]    5
Frank
  • 66,179
  • 8
  • 96
  • 180
Brian
  • 195
  • 8

2 Answers2

2

This should work however further complexity would require changes to the function:

library(data.table) 
dat <- data.frame(Loc = c(51,51,51,51,51), Day = c("Mon","Mon","Tue","Tue","Wed"),
                  Qty = c(1,2,3,4,5), stringsAsFactors = F)
setDT(dat)

comb_in <- function(Qty_In,Day_In){
    temp_df <- aggregate(Qty_In ~ Day_In, cbind(Qty_In, as.character(Day_In)), paste, collapse = "|")
    temp_list <- strsplit(temp_df$Qty_In, split = "|", fixed = T)
    names(temp_list) <- as.character(temp_df$Day)
    melt(as.data.table(expand.grid(temp_list))[, case_group := .I], id.vars = "case_group", variable.name = "Day", value.name = "Qty")
}

dat[, comb_in(Qty_In = Qty, Day_In = Day), by = Loc][order(Loc,case_group,Day)]
    Loc case_group Day Qty
 1:  51          1 Mon   1
 2:  51          1 Tue   3
 3:  51          1 Wed   5
 4:  51          2 Mon   2
 5:  51          2 Tue   3
 6:  51          2 Wed   5
 7:  51          3 Mon   1
 8:  51          3 Tue   4
 9:  51          3 Wed   5
10:  51          4 Mon   2
11:  51          4 Tue   4
12:  51          4 Wed   5

You can now filter by case_group to get each combination

Chris
  • 6,302
  • 1
  • 27
  • 54
  • Very nice solution and does exactly what I needed. Thank you very much! Nice touch adding the `case_group` too. The only other complexities I'll need to introduce are a few other `by` levels, which I was able to update into your code on my own. – Brian Apr 13 '16 at 01:01
0

this question is quite similar to How to expand.grid on vectors sets rather than single elements

for a general approach (performance likely to be slower than a problem specified approach):

permu.sets <- function(listoflist) {
    #assumes that each list within listoflist contains vectors of equal lengths
    temp <- expand.grid(listoflist)   
    do.call(cbind, lapply(temp, function(x) do.call(rbind, x)))
} #permu.sets

#for the problem posted in OP
dat <- data.frame(Loc = c(51,51,51,51,51), Day = c("Mon","Mon","Tue","Tue","Wed"),
    Qty = c(1,2,3,4,5))
vecsets <- lapply(split(dat, dat$Day), function(x) split(as.matrix(x), row(x)))
res <- permu.sets(vecsets)
lapply(split(res, seq(nrow(res))), function(x) matrix(x, ncol=3, byrow=T ))
Community
  • 1
  • 1
chinsoon12
  • 25,005
  • 4
  • 25
  • 35