0

Does anyone know of a way to add up combinations of numbers within a vector?

Suppose I am going through a for loop and each time I end up with a vector of different lengths, how could I combine each element of this vector such that I have the sum of 2, 3, etc elements?

For example if I have:

vector <- c(1:5)

And want to go through it as in:

element 1 + element 2; element 2 + element 3, etc

But also:

element 1 + element 2 + element 3

How would I do this? It's important to note that in many of the vectors the lengths will be different. So whilst one vector might contain 3 elements another might contain 12.

I know you can do vector[1]+vector[2], but I need some way to iterate throughout the vector wherein it takes into account the above note.

coatless
  • 20,011
  • 13
  • 69
  • 84
Gotmadstacks
  • 359
  • 6
  • 20

4 Answers4

3

Use you can use combn:

> combn(vector, 3, FUN = NULL, simplify = TRUE)
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]    1    1    1    1    1    1    2    2    2     3
[2,]    2    2    2    3    3    4    3    3    4     4
[3,]    3    4    5    4    5    5    4    5    5     5

The trick here is that each call will return a matrix of results, and you will have to decide how you want to aggregate and store all the various combinations.

If you don't mind having a list, then the following should do the trick:

> sapply(c(1:length(vector)),
         function(x) {
             combn(vector, x, FUN = NULL, simplify = TRUE)
         })
Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360
3

Generate pair IDs

In this case, we need to get the pairs:

combn(3, 2)

Output:

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

Pairs are generated by column.

Sum Over Vector Elements (Using a Subset)

To access each element and perform a summation, we opt to define a helper function that takes the combination and the vector.

# Write a helper function 
# sums of the index of the vector
comb_subset_sum = function(x, vec){
  return(sum(vec[x]))
}

From this, we can use combn directly or use sapply.

Summing for 1 k:

combn directly:

# Input Vector
vec = 1:5

# Length of vector
n = length(vec)

# Generate pairwise combinations and obtain pair_sum
# Specify the k (m in R)
m = combn(n, m = 2, FUN = comb_subset_sum, vec = vec)

sapply usage:

# Input Vector
vec = 1:5

# Number of Observations
n = length(vec)

# Combinations
# Specify the k (m in R)
combinations = combn(n, m = 2)

# Obtain vectorized sum over subset
subset_summed = apply(combinations, 2, comb_subset_sum, vec = vec)

Example Output:

combinations:

     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
[1,]    1    1    1    1    2    2    2    3    3     4
[2,]    2    3    4    5    3    4    5    4    5     5

subset_summed:

 [1] 3 4 5 6 5 6 7 7 8 9

Trace:

vec[1]+vec[2]=3 
vec[1]+vec[3]=4 
vec[1]+vec[4]=5 
vec[1]+vec[5]=6 
vec[2]+vec[3]=5 
vec[2]+vec[4]=6 
vec[2]+vec[5]=7 
vec[3]+vec[4]=7 
vec[3]+vec[5]=8 
vec[4]+vec[5]=9 

To obtain the trace output, add the following before return() in comb_subset_sum():

cat(paste0("vec[",x,"]", collapse = "+"), "=", sum(vec[x]), "\n")

Summing for multiple k:

Here, we apply the same logic, just in a way that enables the k value of the combination to take multiple values.

# Input Vector
vec = 1:5

# Length of Vec
n = length(vec)

# Store output
o = vector('list',n)

for(i in seq_along(vec)){
  o[[i]] = combn(n, i, FUN = comb_subset_sum, vec = vec)
}

Note: The size of each element of o will vary as the number of combinations will increase and then decrease.

Summing over combinations

If we do not care about vector element values, we can then just sum over the actual combinations in a similar way to how we obtained the vector elements.

To generate pairs and then sum, use:

# Input Vector
vec = 1:5

# Length of Vec
n = length(vec)

# Generate all combinations (by column)
# Specify the k (m in R)
m = combn(n, m = 2)

# Obtain sum by going over columns
sum_m = apply(m, 2, sum)

Or do it in one go:

# Specify the k (m in R)
sum_inplace = combn(n, m = 2, FUN = sum)

Equality:

all.equal(sum_m,sum_inplace)

Sum over k uses

And, as before, we can set it up to get all sums under different k by using:

# Input Vector
vec = 1:5

# Length of Vec
n = length(vec)

# Store output (varying lengths)
o = vector('list',n)

for(i in seq_along(vec)){
  o[[i]] = combn(n, i, FUN = sum)
}
coatless
  • 20,011
  • 13
  • 69
  • 84
  • Hi. I don't really understand your answer. In the example in my question the maximum possible value would be 15 (5+4+3+2+1) but when I run your code I get some values as high as 40. I just need some way to add up all the different combinations of elements within a vector, but in a for loop wherein the vector have x number of elements. Perhaps I have misunderstood? – Gotmadstacks Jul 05 '16 at 15:46
  • Consider `C(5,3)` via `choose(5,3)` this gives a total number of combinations of 10. Thus, for each of those combinations, we get a different `vec` element IDs. We use those element ID to obtain a subset in `vec` and then `sum` over it. Thus, for `C(5,3)` we should have a `numeric` vector of length `10` that contains 10 distinct vector sums. – coatless Jul 05 '16 at 15:53
  • See the additional trace information @Gotmadstacks for clarification as to what the first bit is doing. – coatless Jul 05 '16 at 16:06
1

The following relies on the binary representation of number. Basically, you have 2^n combinations to check. By writing any number between 1 and 2^n in binary with 'n' bits, you have all the permutations of elements you might want.

The number2binary function comes from Paul Hiestra's answer in this tread: How to convert integer number into binary vector?

number2binary = function(number, noBits) {
  binary_vector = rev(as.numeric(intToBits(number)))
  if(missing(noBits)) {
    return(binary_vector)
  } else {
    binary_vector[-(1:(length(binary_vector) - noBits))]
  }
}

vector <- 1:5

n <- length(vector)

comp_sum <- function(x) {
  binary <- number2binary(x, noBits = n)
  result <- sum(vector[which(binary==1)])
  names(result) <- paste(which(binary == 1), collapse = "+")
  return(result)
}

binaries <- sapply(1:2^n-1, comp_sum)

Note: I only go up to 2^n - 1 as you do not need the "zero". By adding some conditions in your comp_sum function, you can pick only sums of two elements or of three elements...

Community
  • 1
  • 1
Choubi
  • 640
  • 3
  • 9
0

You might be looking for rollsum from zoo package, where you can specify the number of elements you want to add up:

lapply(2:5, function(i) zoo::rollsum(1:5, i))
[[1]]
[1] 3 5 7 9    # two elements roll sum

[[2]]
[1]  6  9 12   # three elements roll sum

[[3]]
[1] 10 14      # four elements roll sum

[[4]]
[1] 15         # five elements roll sum
Psidom
  • 209,562
  • 33
  • 339
  • 356