Reputation: 841
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
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
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
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