Mark Miller
Mark Miller

Reputation: 13113

average array elements by groups

I would like to average elements in a 3-dimensional array among groups of sheets or pages (my name for the third dimension).

Below is an example array called my.array. In this example I want to average elements among groups of 3 sheets giving the desired.result. My actual data is in an array with dozens of sheets.

I suspect there is an apply-like function that can do the averaging. However, I am not familiar with using those functions on arrays. Thank you for any advice.

i <- 4
j <- 4
k <- 6

my.array <- array(0, c(i,j,k))

my.array[1:2,1:2,1] <-   1
my.array[1:2,3:4,1] <-   2
my.array[3:4,1:2,1] <-   3
my.array[3:4,3:4,1] <-   4
my.array[1:2,1:2,2] <-  10
my.array[1:2,3:4,2] <-  20
my.array[3:4,1:2,2] <-  30
my.array[3:4,3:4,2] <-  40
my.array[1:2,1:2,3] <- 100
my.array[1:2,3:4,3] <- 200
my.array[3:4,1:2,3] <- 300
my.array[3:4,3:4,3] <- 400

my.array[1:2,1:2,4] <-  -4
my.array[1:2,3:4,4] <-  -3
my.array[3:4,1:2,4] <-  -2
my.array[3:4,3:4,4] <-  -1
my.array[1:2,1:2,5] <-  80
my.array[1:2,3:4,5] <-  70
my.array[3:4,1:2,5] <-  60
my.array[3:4,3:4,5] <-  50
my.array[1:2,1:2,6] <- 111
my.array[1:2,3:4,6] <- 222
my.array[3:4,1:2,6] <- 333
my.array[3:4,3:4,6] <- 444

desired.result <- array(0, c(i,j,k/3))

desired.result[1:2,1:2,1] <-  37        # 111 / 3
desired.result[1:2,3:4,1] <-  74        # 222 / 3
desired.result[3:4,1:2,1] <- 111        # 333 / 3
desired.result[3:4,3:4,1] <- 148        # 444 / 3
desired.result[1:2,1:2,2] <-  62.33333  # (111 +  80 + -4) / 3
desired.result[1:2,3:4,2] <-  96.33333  # (222 +  70 + -3) / 3
desired.result[3:4,1:2,2] <- 130.3333   # (333 +  60 + -2) / 3
desired.result[3:4,3:4,2] <- 164.3333   # (444 +  50 + -1) / 3

, , 1

     [,1] [,2] [,3] [,4]
[1,]   37   37   74   74
[2,]   37   37   74   74
[3,]  111  111  148  148
[4,]  111  111  148  148

, , 2

          [,1]      [,2]      [,3]      [,4]
[1,]  62.33333  62.33333  96.33333  96.33333
[2,]  62.33333  62.33333  96.33333  96.33333
[3,] 130.33330 130.33330 164.33330 164.33330
[4,] 130.33330 130.33330 164.33330 164.33330

This code comes close:

t(sapply(seq(1,2), function(i) {apply( my.array[,,(3*(i-1)+1):(3*i)], c(1,2), mean)}))

         [,1]     [,2]     [,3]     [,4]     [,5]     [,6]     [,7]     [,8]     [,9]    [,10]    [,11]    [,12]    [,13]    [,14]    [,15]    [,16]
[1,] 37.00000 37.00000 111.0000 111.0000 37.00000 37.00000 111.0000 111.0000 74.00000 74.00000 148.0000 148.0000 74.00000 74.00000 148.0000 148.0000
[2,] 62.33333 62.33333 130.3333 130.3333 62.33333 62.33333 130.3333 130.3333 96.33333 96.33333 164.3333 164.3333 96.33333 96.33333 164.3333 164.3333

I prefer a solution in base R.

Upvotes: 1

Views: 92

Answers (2)

Rich Scriven
Rich Scriven

Reputation: 99351

What about just wrapping the sapply call with the desired array? It uses only base R functions.

> array(sapply(seq(1,2), function(i) {
      apply( my.array[,,(3*(i-1)+1):(3*i)], c(1,2), mean)
      }), c(i,j,k/3))

Upvotes: 1

droopy
droopy

Reputation: 2818

one possibility is to use a factor that will identify the group of "sheets" and use the abind fonction from the package abind :

res <- tapply(1:(dim(my.array)[3]), gl(2,3), function(x) rowMeans(my.array[,,x], dim=2))
res <- do.call(abind, c(res, along=3))

HTH

Upvotes: 1

Related Questions