HSJ
HSJ

Reputation: 841

How to aggregate matrices which have different dimensions? [R]

I have a few hundred thousands of matrices in a list which should be aggregated by group identifier. For instance, there are two matrices with different dimentions.

a <- matrix(c(1:12),nrow=3,ncol=4,dimnames=list(c(0:2),c(0:3)))
b <- matrix(c(1:6),nrow=2,ncol=3,dimnames=list(c(0:1),c(0:2)))

> a
  0 1 2  3
0 1 4 7 10
1 2 5 8 11
2 3 6 9 12
> b
  0 1 2
0 1 3 5
1 2 4 6

Do you know how to aggregate those matrices to obtain following matrix in a simple way?

c <- a + b
> c
   0  1  2  3
0  2  7 12 10
1  4  9 14 11
2  3  6  9 12

It may not be such a difficult question, however I could not find the solution.
Row and column names start from 0 which sequentially step-up by 1 but maximum number varies based on each element. Unmatched dimentions in smaller matrices can be padded by 0.
I suppose I can aggregate them by group but cannot aggregate matrices with different dimentions.

Upvotes: 1

Views: 669

Answers (3)

G. Grothendieck
G. Grothendieck

Reputation: 269852

Here are some alternative solutions:

1) Convert each to long form giving both and then use tapply to aggregate and convert back to wide form:

both <- rbind(as.data.frame.table(a), as.data.frame.table(b))
tapply(both[[3]], both[-3], sum, default = 0)

giving:

    Var2
Var1 0 1  2  3
   0 2 7 12 10
   1 4 9 14 11
   2 3 6  9 12

2) This creates a matrix zero having the shape of the result and then defines a function upleft which inserts its first argument into the upper left of zero. Finally we add them together.

upleft <- function(x, zero) replace(zero, cbind(c(row(x)), c(col(x))), x)
zero <- array(0, pmax(dim(a), dim(b)))
upleft(a, zero) + upleft(b, zero)

giving:

     [,1] [,2] [,3] [,4]
[1,]    2    7   12   10
[2,]    4    9   14   11
[3,]    3    6    9   12

Upvotes: 1

phil_t
phil_t

Reputation: 861

You could use this function that takes any two matrices, adjusts the number of rows and columns, pads them with 0s, and then adds them up.

sum_mat = function(a, b){
    temp = matrix(data = 0, nrow = max(nrow(a), nrow(b)), ncol = max(ncol(a), ncol(b)))
    temp_a = temp
    temp_a[1:nrow(a), 1:ncol(a)] = a
    temp_b = temp
    temp_b[1:nrow(b), 1:ncol(b)] = b
    temp_a + temp_b
}


> a
  0 1 2  3
0 1 4 7 10
1 2 5 8 11
2 3 6 9 12

> b
  0 1 2
0 1 3 5
1 2 4 6

c = sum_mat(a, b)

> c
   0  1  2  3
0  2  7 12 10
1  4  9 14 11
2  3  6  9 12

Upvotes: 1

Mark
Mark

Reputation: 4537

Here's a function which takes in two matrices and pads them with 0's to make their dimensions the same. Then it sums them. This can be used with Reduce to sum many together in one operation.

First, get the max number of rows and the max number of columns of the two matrices. Then, a matrix of 0's is created with these max dimensions. Then, only the sub-matrix that matches each input is populated. The matrices are summed and returned.

sum_ragged_matrix = function(m1,m2){
  m1r = nrow(m1r)
  m2r = nrow(m2r)

  m1c = ncol(m1c)
  n2c = ncol(m2c)

  max_rows = max(c(m1r,m2r))
  max_cols = max(c(m1c,m2c))
  t1 = matrix(0,nrow = max_rows,ncol = max_cols)
  t2 = t1
  t1[1:m1r,1:m1c] = m1
  t2[1:m2r,1:m2c] = m2
  return(t1+t2)
}

a = matrix(c(1:12),nrow=3,ncol=4,dimnames=list(c(0:2),c(0:3)))
b = matrix(c(1:6),nrow=2,ncol=3,dimnames=list(c(0:1),c(0:2)))
c = matrix(c(1:4),nrow=2,ncol=2,dimnames=list(c(0:1),c(0:1)))

Reduce(sum_ragged_matrix,list(a=a,b=b,c=c),init=matrix(0,nrow=1,ncol=1))

Upvotes: 1

Related Questions