1

I have a list of matrices of equal size:

a <- matrix(data = c(1,2,3,4,5,6,7,8,9), ncol = 3, nrow = 3)

b <- matrix(data = c(9,8,7,6,5,4,3,2,1), ncol = 3, nrow = 3)

c <- matrix(data = c(1,2,3,4,5,6,7,8,9), ncol = 3, nrow = 3)

d <- matrix(data = seq(from = 1, to = 9, by = 1), ncol = 3, nrow = 3)

e <- matrix(data = seq(from = 10, to = 90, by = 10), ncol = 3, nrow = 3)

f <- matrix(data = seq(from = 9, to = 1, by = -1), ncol = 3, nrow = 3)


test_list <- list(a, b, c, d, e, f)

How can I sum every set of three matrices so that the output is two matrices, the first being the sum of a, b and c (output_1) and the second the sum of d, e and f (output_2)? Ideally the output would be a new list of two matrices, e.g.

output_1 <- structure(c(11, 12, 13, 14, 15, 16, 17, 18, 19), .Dim = c(3L, 
3L)) 

output_2 <- structure(c(3L, 6L, 9L, 12L, 15L, 18L, 21L, 24L, 27L), .Dim = c(3L, 
3L))
John L. Godlee
  • 559
  • 5
  • 21
  • 1
    The last three vectors illustrate a typical error in the use of `seq`. The `seq` function is intended to take a single integer for its first argument. When it is presented only a vector of length greater than 1 it gets interpreted as `seq_along` – IRTFM Nov 06 '18 at 16:52
  • Eeesht, that is not what i wanted, a careless edit. Fixing – John L. Godlee Nov 07 '18 at 09:40
  • It's going to change what the answer will be. Looking at what I originally _thought_ would be teh [1,1] value for the second matrix (1+10+9) and seeing instead `3` was what made me write my first comment. – IRTFM Nov 07 '18 at 09:51

5 Answers5

4
library(purrr)
map(split(test_list, ceiling(seq_along(test_list)/3)), ~reduce(.x , `+`))

$`1`
     [,1] [,2] [,3]
[1,]   11   14   17
[2,]   12   15   18
[3,]   13   16   19

$`2`
     [,1] [,2] [,3]
[1,]    3   12   21
[2,]    6   15   24
[3,]    9   18   27

Credit to this answer for the neat splitting code.

zack
  • 5,205
  • 1
  • 19
  • 25
  • 1
    I've never really understood `map`. In this case, what's the difference between this and `lapply(split(test_list, ceiling(seq_along(test_list)/3)), reduce, `+`)`? – iod Nov 06 '18 at 16:31
  • 1
    Not much difference in this case, [this thread](https://stackoverflow.com/questions/45101045/why-use-purrrmap-instead-of-lapply) is a good read. To quote Hadley's answer on that thread: "Personally, I find that when I use purrr, I can write functional code with less friction and greater ease; it decreases the gap between thinking up an idea and implementing it. But your mileage may vary; there's no need to use purrr unless it actually helps you." – zack Nov 06 '18 at 16:35
  • Really like this answer because it illustrates a map-reduce logic. I am a bit puzzled about it's location at the bottom of the list of answers at the moment. I thought the SO ordering of answers gave weight to the number of upvotes. – IRTFM Nov 06 '18 at 16:40
  • @42- There are three ways to order answers. Default is the newest(active, which means last one edited). But you can change to votes or see the oldest. – M-- Nov 06 '18 at 16:54
  • 2
    I would like to point out that `map` is not equivalent to `Map` or its predecessor, `mapply`. Whereas `reduce` appears equivalent to `Reduce`. – IRTFM Nov 06 '18 at 16:56
2

43 seconds ago @iod commented the same thought I had. I present this as a base equivalent to the map-reduce-logic in zack's answer:

lapply( split( test_list, cumsum( rep( c(T,F,F), 2))), function(x){ Reduce( "+", x)})
$`1`
     [,1] [,2] [,3]
[1,]   11   14   17
[2,]   12   15   18
[3,]   13   16   19

$`2`
     [,1] [,2] [,3]
[1,]    3   12   21
[2,]    6   15   24
[3,]    9   18   27

Mine used "Reduce" which is described on a help page with a group of "funprog"-functions entitled "Common Higher-Order Functions in Functional Programming Languages". Like iod, I've sometimes had some difficulty with the Map, and Reduce functions. In this case one needs to use Reduce rather than do.call because "+" is a binary operator and cannot handle vectors with 3 items. Internally its not really that different than the double loop answer of r.user.05apr. Both lapply-split and Reduce are really loops in disguise.

There are now several examples of different mechanisms for construction of "groups of 3". My approach was to construct a logical vector and then run cumsum to make a numeric vector. Masoud used gl. zack used ceiling(seq_along(test_list)/3) and politely credited its inspiration.

IRTFM
  • 258,963
  • 21
  • 364
  • 487
2

Just a doodle with data.table:

data.table::rbindlist(lapply(test_list, function(x) as.data.frame(x)))[, as.list(colSums(.SD)), 
                                     by = gl(ceiling(length(test_list)*nrow(test_list[[1]])/3), 
                                     3, length(test_list)*nrow(test_list[[1]]))]
M--
  • 25,431
  • 8
  • 61
  • 93
  • 1
    Using `gl` reminds me of my days using GLIM. GLIM was excellent preparation for learning R. – IRTFM Nov 06 '18 at 16:46
  • @42- I am still learning `R` in a very broad term, including using `gl`. Thanks for letting me know that I am on the right path (I see you as an expert). Cheers. – M-- Nov 06 '18 at 16:50
  • 1
    Looks to me that you are a quick study. Your isochronic-contours google map Q&A seemed pretty sophisticated to me. – IRTFM Nov 06 '18 at 17:16
1

Transform into an array and apply rowSums:

test_array<-array(c(a,b,c,d,e,f),c(3,3,6))
apply(test_array[,1:3,1:3],2,rowSums)

     [,1] [,2] [,3]
[1,]   11   14   17
[2,]   12   15   18
[3,]   13   16   19

apply(test_array[,1:3,4:6],2,rowSums)

     [,1] [,2] [,3]
[1,]    3   12   21
[2,]    6   15   24
[3,]    9   18   27
iod
  • 7,412
  • 2
  • 17
  • 36
1

Or:

Matrix_Number <- 6

res <- vector("list", Matrix_Number / 3)
for (i in (1:(Matrix_Number/3))) {
  res[[i]] <- test_list[[(i-1)*3 + 1]] + test_list[[(i-1)*3 + 2]] + test_list[[(i-1)*3 + 3]]
}
res
r.user.05apr
  • 5,356
  • 3
  • 22
  • 39