8  Matrices and arrays

Matrices and arrays are fundamental data structures in R that enable efficient storage and manipulation of multi-dimensional data. Throughout this chapter, we explore how to create, manipulate, and perform operations on matrices and arrays.

 

8.1 Definition of a matrix

In mathematics, a matrix is a rectangular array of elements arranged in rows and columns. A matrix is defined by its dimensions, which specify the number of rows and columns it contains. For example:

X=[x11x12x13x14x21x22x23x24x31x32x33x34]3×4

The matrix X is a 3×4 matrix because it has 3 rows and 4 columns. For example, the element in the first row and second column is denoted as x12, and the element in the second row and third column is denoted as x23.

Then 3×1 matrices [x11x21x31], [x12x22x32], [x13x23x33], [x14x24x34] are called column vectors of the matrix.

Also 1×4 matrices such that [x11x12x13x14], [x21x22x23x24], [x31x32x33x34] are called row vectors of the matrix.

The main diagonal of a matrix refers to the collection of elements that run from the top-left corner to the bottom-right corner of the matrix. In other words, it is a sequence of elements where the row index and the column index are the same (xij where i=j). In the above example, the elements of the main diagonal are: x11,x22,and x33.

 

Example

X=[402131422013]3×4

 

  • The column vectors are: [432], [010], [241], [123]

  • The row vectors are: [4021], [3142], [2013]

  • The main diagonal consisted of the numbers 4, 1, and 1.

8.2 Creating a matrix in R

In R, each data object has various attributes that describe its characteristics and structure. For example, matrices are created using the dim (dimension) attribute, which facilitates matrix algebra operations.

Matrix

A matrix is a two-dimensional atomic vector used to represent data organized into rows and columns, where all elements are of the , such as numeric, character, or logical.

 

Adding a dimension attribute of 2 to an atomic vector allows it to be reshaped into a two-dimensional matrix. For example:

X1 <- c(4, 3, 2, 0, 1, 0, 2, 4, 1, 1, 2, 3)

dim(X1) <- c(3, 4)

X1
     [,1] [,2] [,3] [,4]
[1,]    4    0    2    1
[2,]    3    1    4    2
[3,]    2    0    1    3

The dim() is an in-built R function that either sets or returns the dimension of the matrix, array, or data frame. Here, the dim() function sets the dimension for the X1 object.

Most often we create a matrix using the matrix() function. In this case, we need to specify the number of rows and columns in the function.

Example: numeric matrix

x_numeric <- c(4, 3, 2, 0, 1, 0, 2, 4, 1, 1, 2, 3)

X2 <- matrix(x_numeric, nrow = 3, ncol = 4)
X2
     [,1] [,2] [,3] [,4]
[1,]    4    0    2    1
[2,]    3    1    4    2
[3,]    2    0    1    3

The matrix is filled by columns (default column-wise), so entries can be thought of starting in the “upper left” corner and running down the columns. If we want the matrix to be filled by rows we must add the extra argument byrow = TRUE in the matrix() function, as follows:

X3 <- matrix(x_numeric, nrow = 3, ncol = 4, byrow = TRUE)
X3
     [,1] [,2] [,3] [,4]
[1,]    4    3    2    0
[2,]    1    0    2    4
[3,]    1    1    2    3

The type of data, the class and the dimension of the X3 object are:

typeof(X3)
[1] "double"
class(X3)
[1] "matrix" "array" 

Note that the typeof() function returns the data type contained within an object (i.e., double), while the class() function returns the structural type (i.e., matrix) of the object.

dim(X3)
[1] 3 4

In this example, the dim() function takes the R object X3 as an argument and returns its dimension.

 

Example: logical matrix

x_logical <- c(TRUE, FALSE, FALSE, TRUE, FALSE, FALSE)
X4 <- matrix(x_logical, nrow = 2, ncol = 3)
X4
      [,1]  [,2]  [,3]
[1,]  TRUE FALSE FALSE
[2,] FALSE  TRUE FALSE

The data type of the X4 object is:

typeof(X4)
[1] "logical"

 

Example: character matrix

x_char <- c("a", "b", "c", "d", "e", "f")
X5 <- matrix(x_char, nrow = 2, ncol = 3)
X5
     [,1] [,2] [,3]
[1,] "a"  "c"  "e" 
[2,] "b"  "d"  "f" 

The data type of the X5 object is:

typeof(X5)
[1] "character"

8.3 Using matrix subscripts

In R, we can access rows, columns, or individual elements of a matrix using subscripts within brackets. Specifically, X[i, ] refers to the ith row of matrix X, X[ , j] refers to jth column, and X[i, j] refers to the ij-element, as shown in the following examples.

# create a 2x5 numeric matrix filled by column
X <- matrix(1:10, nrow=2)  
X
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    3    5    7    9
[2,]    2    4    6    8   10
X[2, ]   # select the 2nd row
[1]  2  4  6  8 10
X[, 2]  # select the 2nd column
[1] 3 4
X[1, 4]  # select the element in the 1st row, 4th column
[1] 7

Furthermore, we can use numeric vectors within the brackets to select multiple rows and/or columns, as shown in the example below:

X[1, c(4, 5)]  # select the elements in the 1st row, 4th and 5th column 
[1] 7 9

 

INFO

Similar to atomic vectors, square brackets [ ] are used to select individual or multiple elements from a matrix. Since matrices have two dimensions, both the row(s) and column(s) must be specified, separated by a in the format [row(s), column(s)].

 

8.4 Special types of matrices

8.4.1 The square matrix

A square matrix is a matrix that has an equal number of rows and columns.

Example

M=[510312401]3×3

In R:

M <- matrix( c(5, 3, 4, 1, -1, 0, 0, 2, -1), nrow = 3)
M
     [,1] [,2] [,3]
[1,]    5    1    0
[2,]    3   -1    2
[3,]    4    0   -1

The main diagonal consists of the numbers 5, -1, and -1. In R, we can extract the diagonal elements of a matrix using the diag() function, as follows:

diag(M)
[1]  5 -1 -1

The trace of a square matrix is the sum of its main diagonal elements. In the example above, the trace is Tr=5+(1)+(1)=52=3, which can be calculated in R using the following code:

sum(diag(M))
[1] 3

 

8.4.2 The diagonal matrix

A diagonal matrix is a special type of square matrix where all the elements outside the main diagonal are zero.

Example

D=[400010005]3×3

In R, we can create a diagonal matrix of size 3 using the diag() function as follows:

elements <- c(4, -1, -5)
D <- diag(elements)
D
     [,1] [,2] [,3]
[1,]    4    0    0
[2,]    0   -1    0
[3,]    0    0   -5

 

8.4.3 The identity matrix

An identity matrix, often denoted as I, is a square matrix with ones on the main diagonal and zeros elsewhere. It is a special case of a diagonal matrix, where all diagonal elements equal 1.

Example

I=[100010001]3×3

In R, we can create the identity matrix of size 3 by using the diag() function:

I <- diag(3)
I
     [,1] [,2] [,3]
[1,]    1    0    0
[2,]    0    1    0
[3,]    0    0    1

 

8.4.4 Symmetric matrix

A symmetric matrix is a square matrix that remains unchanged when we transpose it (see also section 8.5.1), which means we swap its rows and columns.

Example

S=[13424112228]3×3 In R:

S <- matrix(c(13, -4, 2, -4, 11, -2, 2, -2, 8), nrow = 3)
S
     [,1] [,2] [,3]
[1,]   13   -4    2
[2,]   -4   11   -2
[3,]    2   -2    8

In S matrix, the elements at positions (1,2) and (2,1) are both -4, the elements at positions (1,3) and (3,1) are both 2, and the elements at positions (2,3) and (3,2) are both -2. This reflects the symmetry property.

8.5 Basic matrix algebra

8.5.1 The transpose of a matrix

The transpose operation swaps the rows and columns of an n×p matrix, resulting in a new matrix with dimensions p×n.

Example

Given the matrix A:

A=[415012]2×3

The transpose of A, denoted as A, is obtained by interchanging the rows and columns, so that the first column of A becomes the first row of A, the second column becomes the second row, and so forth.

A=[401152]3×2

In R:

We define the matrix A:

A <- matrix(c(4, 0, -1, 1, -5, -2), nrow = 2)
A
     [,1] [,2] [,3]
[1,]    4   -1   -5
[2,]    0    1   -2

To compute the transpose of matrix A, we can use the t() function:

t(A)
     [,1] [,2]
[1,]    4    0
[2,]   -1    1
[3,]   -5   -2

 

8.5.2 Matrix addition

Matrix addition is an operation performed on two matrices with the same dimensions. It involves adding the corresponding elements of the matrices (element-wise addition) to create a new matrix of the same dimension.

Example

Consider the following matrices A and B:

A=[415012]2×3

B=[315022]2×3

To add A and B, we perform element-wise addition by summing the corresponding elements, as shown below:

A+B=[415012]2×3+[315022]2×3=[7010034]2×3

In the resulting matrix, the element in the first row and first column is 4+3=7, the element in the first row and second column is 1+1=0, the element in the first row and third column is 5+(5)=10. The elements in the second row are calculated similarly.

In R:

A
     [,1] [,2] [,3]
[1,]    4   -1   -5
[2,]    0    1   -2
B <-matrix(c(3, 0, 1, 2, -5, -2), nrow = 2)
B
     [,1] [,2] [,3]
[1,]    3    1   -5
[2,]    0    2   -2

To perform the addition of matrices A and B, we simply use the + operator:

A + B
     [,1] [,2] [,3]
[1,]    7    0  -10
[2,]    0    3   -4

 

8.5.3 Scalar multiplication of matrices

In scalar multiplication, each element of a matrix is multiplied by a given scalar (a constant number). For example:

Example

Consider the scalar multiplication of matrix A by -3:

3A=3[415012]2×3=[12315036]2×3

In the resulting matrix, the element in the first row and first column is 34=12, the element in the first row and second column is 3(1)=3, the element in the first row and third column is 3(5)=15, and so on.

In R:

A
     [,1] [,2] [,3]
[1,]    4   -1   -5
[2,]    0    1   -2
-3 * A
     [,1] [,2] [,3]
[1,]  -12    3   15
[2,]    0   -3    6

 

8.5.4 Hadamard product

The Hadamard product is the element-wise multiplication of two matrices, A and B, that have the same dimensions. It is denoted by the operator and involves multiplying corresponding elements of the two matrices to produce a new matrix with the same dimensions.

Example

AB=[415012]2×3[315022]2×3=[12125024]2×3

In the resulting matrix, the element in the first row and first column is 43=12, the element in the first row and second column is 11=1, the element in the first row and third column is 5(5)=25. The second row elements are computed similarly.

In R:

A
     [,1] [,2] [,3]
[1,]    4   -1   -5
[2,]    0    1   -2
B
     [,1] [,2] [,3]
[1,]    3    1   -5
[2,]    0    2   -2

The output will be a new matrix with the same dimensions as the original matrices:

A * B
     [,1] [,2] [,3]
[1,]   12   -1   25
[2,]    0    2    4

 

8.5.5 Matrix product

Suppose A is a n×k matrix and C is a k×p matrix, where the number of columns in the first matrix is equal to the number of rows in the second matrix (i.e., compatible matrices). The matrix product of A and C is denoted as AC, representing the standard matrix multiplication. Each element of the matrix AC is formed by taking the dot product of a row of A with a column of C (i.e., row-by-column multiplication). Let’s illustrate this procedure with examples.

IMPORTANT

Before multiplying two matrices, we must ensure that their dimensions are compatible. The number of columns in the first matrix must equal the number of rows in the second matrix.

 

Example: compatible matrices

Suppose we have the following matrices A and C:

A=[415012]2×3

C=[552120]3×2

The row-by-column multiplication of these two matrices results in a new matrix:

AC=[415012]2×3[552120]3×2=[121961]2×2

We observe that the resulting matrix has dimensions 2×2. Now, let’s explain the process of this type of multiplication:

  • the element in the first row and first column of the new matrix is the result of the dot product between the first row of A and the first column of C: [415][522]=4(5)+(1)2+(5)(2)=202+10=12

  • the element in the first row and second column of the new matrix is the result of the dot product between the first row of A and the second column of C: [415][510]=45+(1)1+(5)0=201+0=19

  • the element in the second row and first column of the new matrix is the result of the dot product between the second row of A and the first column of C: [012][522]=0(5)+12+(2)(2)=0+2+4=6

  • the element in the second row and second column of the new matrix is the result of the dot product between the second row of A and the second column of C: [012][510]=05+11+(2)0=1

Matrix multiplication can be achieved using the dot product operator %*%.

A
     [,1] [,2] [,3]
[1,]    4   -1   -5
[2,]    0    1   -2
C <- matrix(c(-5, 2, -2, 5, 1, 0), nrow = 3)
C
     [,1] [,2]
[1,]   -5    5
[2,]    2    1
[3,]   -2    0
A %*% C
     [,1] [,2]
[1,]  -12   19
[2,]    6    1

 

Example: multiplication after transposition

Consider the matrices

A=[415012]2×3

and

B=[315022]2×3

These matrices are not compatible for multiplication because the number of columns in A does not match the number of rows in B. However, if we transpose matrix A, we obtain a 3×2 matrix:

A=[401152]3×2

Now, the A and B matrices are compatible, their matrix product is well defined, and we can proceed with the multiplication:

AB=[401152]3×2[315022]2×3=[1242031315929]3×3

In R:

t(A)
     [,1] [,2]
[1,]    4    0
[2,]   -1    1
[3,]   -5   -2
B
     [,1] [,2] [,3]
[1,]    3    1   -5
[2,]    0    2   -2
t(A) %*% B
     [,1] [,2] [,3]
[1,]   12    4  -20
[2,]   -3    1    3
[3,]  -15   -9   29

However, it is more efficient and faster using the crossprod() function:

crossprod(A, B)
     [,1] [,2] [,3]
[1,]   12    4  -20
[2,]   -3    1    3
[3,]  -15   -9   29

 

8.5.6 The inverse of a matrix

Given a square matrix E, its inverse is another square matrix of the same dimensions, denoted as E1, such that when the two matrices are multiplied together, they yield the identity matrix I.

Ek×kEk×k1=Ik×k

Example

Let’s consider a 3x3 matrix E:

E=[111201112]2×2

In R, we can use the generic built-in solve() function to find the inverse of the matrix E:

# create the matrix E
E <- matrix( c(1, 2, 1, -1, 0, 1, 1, 1, 2), nrow = 3)

# the solve() function takes a matrix as input and returns the matrix's inverse
E_inv <- solve(E)
E_inv
      [,1]  [,2]  [,3]
[1,] -0.25  0.75 -0.25
[2,] -0.75  0.25  0.25
[3,]  0.50 -0.50  0.50

Therefore, we can verify that if we multiply the matrix E by its inverse E1, we get back the identity matrix:

 E %*% E_inv
     [,1] [,2] [,3]
[1,]    1    0    0
[2,]    0    1    0
[3,]    0    0    1

 

8.6 Arrays

8.6.1 Creating an array

An array is similar to matrix but can have more than two dimensions. Conceptually, it can be thought of as a stack of matrices, where each matrix has a specific number of rows and columns. These layers are stacked on top of each other, forming the multidimensional structure of the array.

Let’s create an example of an array using the array() function from base R:

# build an 2x3x4 array
my_array <- array(1:24, dim = c(2, 3, 4))
my_array
, , 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

This example demonstrates how arrays extend matrices by providing greater flexibility in data representation. Each layer is a 2x3 matrix, and four such layers are stacked to form the array. Like matrices, arrays contain elements of a single data type (e.g., numeric). We can check the array’s type, class and the dimensions as follows:

typeof(my_array)
[1] "integer"
class(my_array)
[1] "array"
dim(my_array)
[1] 2 3 4

The output confirms a 3-dimensional array of integers with dimensions 2, 3, and 4.

8.6.2 Indexing in an array

To access a specific matrix within the array, for example, the 3rd matrix, we type:

# access the 3rd matrix of the array
my_array[, , 3]
     [,1] [,2] [,3]
[1,]   13   15   17
[2,]   14   16   18


To access the 2nd row of the 3rd matrix:

# access the 2nd row of the 3rd matrix of the array.
my_array[2, , 3]
[1] 14 16 18


To access the element in the 1st row and 3rd column of the 3rd matrix:

# access the element in the 1st row and 3rd column of the 3rd matrix
my_array[1, 3, 3]
[1] 17

8.7 Operations with arrays

Arrays in R support element-wise arithmetic operations, such as addition, subtraction, multiplication, and division, allowing users to perform computations on corresponding elements across arrays.

8.7.1 Addition of arrays

Let’s consider the addition of two arrays, arr1 and arr2, each with dimensions 2×3×2:

arr1 <- array(1:12, dim = c(2, 3, 2))
arr1
, , 1

     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6

, , 2

     [,1] [,2] [,3]
[1,]    7    9   11
[2,]    8   10   12


arr2 <- array(13:24, dim = c(2, 3, 2))
arr2
, , 1

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

, , 2

     [,1] [,2] [,3]
[1,]   19   21   23
[2,]   20   22   24


# Element-wise addition of two arrays
array_addition <- arr1 + arr2
array_addition
, , 1

     [,1] [,2] [,3]
[1,]   14   18   22
[2,]   16   20   24

, , 2

     [,1] [,2] [,3]
[1,]   26   30   34
[2,]   28   32   36

In R, the addition operation for arrays is performed element-wise, where corresponding elements at the same indices in arr1 and arr2 are summed to produce the elements in array_addition.

8.7.2 Multiplication of arrays

Similarly, we can perform element-wise multiplication between two arrays:

# Element-wise multiplication of two arrays
array_multiplication <- arr1 * arr2
array_multiplication
, , 1

     [,1] [,2] [,3]
[1,]   13   45   85
[2,]   28   64  108

, , 2

     [,1] [,2] [,3]
[1,]  133  189  253
[2,]  160  220  288