Sum all possible combinations of columns in R

Viewed 35

I have a data table (100 rows x 25 cols) that is structured like this:

     ColA ColB ColC ColD
1:      1    3    1    2
2:      2    2    1    2
3:      3    1    1    2

I want to add column values together, in every possible combination.

The output would include, for example:

ColA+B ColA+C ColA+D ColB+C ColB+D etc.

BUT! I don't just want pairs. I am trying to get every combination. I also want to see, for example:

ColA+B+C ColA+B+D ColA+C+D ColB+C+D

And:

ColA+B+C+D

Ideally I could simply add all these permutations to the right of the base dataset (I am looking to do a correlation matrix on all these permutations.) I am far from an R expert. I see there are packages like combinat - but they don't seem to get at what I'm after. I would be very grateful indeed for any suggestions.

Thank you.

1 Answers

I'm hesitant to present this as a suggestion: it works with four columns, but as @DanAdams commented, this explodes with 25 columns:

choose(25,2)   # 25 columns, 2 each
# [1] 300
choose(25,3)   # 25 columns, 3 each
# [1] 2300

### 25 columns, in sets of 2 through 25 at a time
sum(sapply(2:25, choose, n=25))
# [1] 33554406

But, let's assume that you can control the number of combinations you need. Change 2:4 to be the number of combinations you need.

combs <- do.call(c, lapply(2:4, function(z) asplit(combn(names(dat), z), 2)))
names(combs) <- sapply(combs, paste, collapse = "_")
length(combs)
# [1] 11
combs[c(1,2,10,11)]
# $ColA_ColB
# [1] "ColA" "ColB"
# $ColA_ColC
# [1] "ColA" "ColC"
# $ColB_ColC_ColD
# [1] "ColB" "ColC" "ColD"
# $ColA_ColB_ColC_ColD
# [1] "ColA" "ColB" "ColC" "ColD"

ign <- Map(function(cols, nm) dat[, (nm) := rowSums(.SD), .SDcols = cols], combs, names(combs))
dat[]
#     ColA  ColB  ColC  ColD ColA_ColB ColA_ColC ColA_ColD ColB_ColC ColB_ColD ColC_ColD ColA_ColB_ColC ColA_ColB_ColD ColA_ColC_ColD ColB_ColC_ColD ColA_ColB_ColC_ColD
#    <int> <int> <int> <int>     <num>     <num>     <num>     <num>     <num>     <num>          <num>          <num>          <num>          <num>               <num>
# 1:     1     3     1     2         4         2         3         4         5         3              5              6              4              6                   7
# 2:     2     2     1     2         4         3         4         3         4         3              5              6              5              5                   7
# 3:     3     1     1     2         4         4         5         2         3         3              5              6              6              4                   7

BTW: I'm inferring that your data is of class data.table, ergo the side-effect I'm using here. If that's not the case, then this is base R:

dat <- cbind(dat, data.frame(lapply(combs, function(cols) rowSums(subset(dat, select = cols)))))
dat
#   ColA ColB ColC ColD ColA_ColB ColA_ColC ColA_ColD ColB_ColC ColB_ColD ColC_ColD ColA_ColB_ColC ColA_ColB_ColD ColA_ColC_ColD ColB_ColC_ColD ColA_ColB_ColC_ColD
# 1    1    3    1    2         4         2         3         4         5         3              5              6              4              6                   7
# 2    2    2    1    2         4         3         4         3         4         3              5              6              5              5                   7
# 3    3    1    1    2         4         4         5         2         3         3              5              6              6              4                   7

(Please don't blame me if your R crashes due to memory exhaustion. Save your work often.)


Data

dat <- setDT(structure(list(ColA = 1:3, ColB = 3:1, ColC = c(1L, 1L, 1L), ColD = c(2L, 2L, 2L)), class = c("data.table", "data.frame"), row.names = c(NA, -3L)))
Related