3.2 Array

Having learned the 1-dimensional atomic vectors in Chapter 2 and 2-dimensional matrix in Section 3.1, we will now take a look in higher dimensional space, namely array. Array can be viewed as an generalization of vector and matrix to a higher dimensional (\(>=3\)) space, and still only contains elements of the same type.

3.2.1 Create an array from a vector

To create an array from a vector, you can use the function array(). Let’s see an example and use dim() to get its dimension.

x <- array(1:24, c(2, 3, 4))
x
#> , , 1
#> 
#>      [,1] [,2] [,3]
#> [1,]    1    3    5
#> [2,]    2    4    6
#> 
#> , , 2
#> 
#>      [,1] [,2] [,3]
#> [1,]    7    9   11
#> [2,]    8   10   12
#> 
#> , , 3
#> 
#>      [,1] [,2] [,3]
#> [1,]   13   15   17
#> [2,]   14   16   18
#> 
#> , , 4
#> 
#>      [,1] [,2] [,3]
#> [1,]   19   21   23
#> [2,]   20   22   24
dim(x)
#> [1] 2 3 4

You can see from the result, array has more than two dimensions. To display all elements, R will slice the array into many matrices by fixing the indices except the first two, then display one matrix at a time.

As usual, we can learn more about an array by using the str(), class(), typeof(), and attributes() functions.

str(x)
#>  int [1:2, 1:3, 1:4] 1 2 3 4 5 6 7 8 9 10 ...
class(x)
#> [1] "array"
typeof(x)
#> [1] "integer"
attributes(x)
#> $dim
#> [1] 2 3 4

We can see that x is a three-dimensional array with integers as its elements. Similarly, you can create a higher-dimensional array. The following example creates a four-dimensional array with all elements equal to 6.

y <- array(6, 2:5)
y
dim(y)

3.2.2 Array subsetting

Like vector subsetting in Section 2.5.2 and matrix subsetting in Section 3.1.3, we can do array subsetting as well.

To do array subsetting, you can specify the indices for each dimension separated by ,. Since x is a three-dimensional array, you can specify at most 3 indices. If you leave the indices for certain dimensions empty, everything will be kept along the corresponding dimensions. You can also use negative index to dropping specified elements. Let’s see a few examples.

x[1, 2, 3]  #the (1, 2, 3)-th element of x
#> [1] 15
x[, , 2]  #the matrix where the index of the 3rd dimension equals 2
#>      [,1] [,2] [,3]
#> [1,]    7    9   11
#> [2,]    8   10   12
x[2, , 4]  #the vector whether the indices of the 1st and 3rd dimension equal 2 and 4.
#> [1] 20 22 24
x[-2, 3, -3]
#> [1]  5 11 23

Just like subsetting matrices, you can also use logical vectors on each dimension, as well as using names as the indices on a named array.

x[x[, 1, 2] == 7, , ]
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    7   13   19
#> [2,]    3    9   15   21
#> [3,]    5   11   17   23

3.2.3 Operators and functions on arrays

Similar to vectors and matrices, arithmetic operations between arrays are performed elementwisely, with the recycling rule applying when the two arrays are of different dimensions. Let’s see the following example.

array_1 <- array(1:8, c(2, 2, 2))
array_2 <- array(8:1, c(2, 2, 2))
array_1 + array_2
#> , , 1
#> 
#>      [,1] [,2]
#> [1,]    9    9
#> [2,]    9    9
#> 
#> , , 2
#> 
#>      [,1] [,2]
#> [1,]    9    9
#> [2,]    9    9
array_1 * array_2
#> , , 1
#> 
#>      [,1] [,2]
#> [1,]    8   18
#> [2,]   14   20
#> 
#> , , 2
#> 
#>      [,1] [,2]
#> [1,]   20   14
#> [2,]   18    8
array_1 * 3
#> , , 1
#> 
#>      [,1] [,2]
#> [1,]    3    9
#> [2,]    6   12
#> 
#> , , 2
#> 
#>      [,1] [,2]
#> [1,]   15   21
#> [2,]   18   24

To apply a function on all elements of an array, you can directly use the function on the array object as if it is a vector, as we did with a matrix. The result is equivalent to first convert the array into a vector using as.vector() and apply the function on the vector.

sum(array_2)
#> [1] 36
mean(array_2)
#> [1] 4.5
quantile(array_2, c(0.25, 0.5, 0.75))
#>  25%  50%  75% 
#> 2.75 4.50 6.25
cumsum(array_2)
#> [1]  8 15 21 26 30 33 35 36

3.2.4 Apply functions along certain dimension(s)

Just like when we work with matrices, it is of interest to apply functions not on the whole array, but along certain dimension(s). To do this, you can use the same function apply() as we did in matrices.

apply(array_1, 1, mean)  #calculate the mean along the first dim
#> [1] 4 5
mean(array_1[1, , ])  #verify the first element
#> [1] 4
mean(array_1[2, , ])  #verify the second element
#> [1] 5
apply(array_1, 2, sum)  #calculate the sum along the second dim
#> [1] 14 22
apply(array_1, 3, sd)  #calculate the sd along the third dim
#> [1] 1.290994 1.290994

In the second argument of apply(), in addition to specifying one dimension, you can also supply a vector of dimensions that the function will be applied upon. For example, the following code computes the sum along the first two dimensions.

apply(array_1, 1:2, sum)
#>      [,1] [,2]
#> [1,]    6   10
#> [2,]    8   12
sum(array_1[1, 1, ])  #verify the [1, 1] element
#> [1] 6
sum(array_1[1, 2, ])  #verify the [1, 2] element
#> [1] 10
sum(array_1[2, 1, ])  #verify the [2, 1] element
#> [1] 8
sum(array_1[2, 2, ])  #verify the [2, 2] element
#> [1] 12

In addition to the three arguments, you can add additional arguments that will be passed when applying the specified function as we did in Section 3.1.6. For example, the follow code generates the 0.75 quantile along the third dimension.

apply(array_1, 3, quantile, 0.75)
#> [1] 3.25 7.25

3.2.5 Exercises

First use R to create the following array

a <- array(1:24, c(2, 3, 4))
  1. Compute the mean along the second dimension of a. (Hint: the result will be a vector of length 3.)
  2. Compute the first quartile along the first and third dimension of a. (Hint: the result will be a matrix of dimension \(2\times 4\))
  3. Compute the first and third quartile along the first and third dimension of a. (Hint: the result will be an array of dimension \((2, 2, 4)\))