Assignment Operations on Matrices

What is a matrix?

Matrix 𝐴𝐴 of dimension (𝑚×n)(𝑚 \times n) is an (𝑚×𝑛)(𝑚 \times 𝑛) collection of elements. The elements are ordered according to a rectangular scheme consisting of 𝑚𝑚 rows and 𝑛𝑛 columns, such as:

[a11a12a1na21a22a2nam1am2amn] \begin{bmatrix} a_{11} & a_{12} & \dots & a_{1n} \\ a_{21} & a_{22} & \dots & a_{2n} \\ \vdots & \vdots & \vdots & \vdots\\ a_{m1} & a_{m2} & \dots & a_{mn} \end{bmatrix}

Here, 𝑎𝑖𝑗𝑎_{𝑖𝑗}, with 𝑖=1,...,𝑚𝑖 = 1,...,𝑚 and 𝑗=1,...,𝑛𝑗 = 1,...,𝑛.

Matrix assignment in R

In base R, the matrix() function instantiates the matrix passing the data and the dimensions, and determines whether the matrix is filled by columns (which is the default) or by rows.

Press + to interact
A <- matrix(c(1, 2, 3, 4, 5, 6),
ncol = 3,
nrow = 2,
byrow = TRUE
)
class(A)
dim(A)
nrow(A)
ncol(A)

Explanation

In the code above:

  • Lines 1–5: We use the matrix() function to create a matrix of dimensions (2×3)(2 \times 3).

  • Line 7: We use the class() function to return the values of the class attribute of an R object.

  • Line 8: We use the dim() function to print the dimensions of the matrix.

  • Lines 9–10: The nrow() and ncol() functions print the number of rows and the number of columns in the matrix, respectively.

The returned object is of class matrix of dimension (2×3)(2 \times 3), that is, 22 rows and 33 columns.


Matrix A

[123456]\begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ \end{bmatrix}

Matrix visualization

According to data science professionals, the first step in studying data is visualization. It is the same for matrix algebra. It is important for us to determine what structure the data uses. If we use the plot.matrix package, the matrix can be visualized as a heatmap.

Let’s try executing the following code to display a randomly distributed heatmap.

Press + to interact
library(plot.matrix)
# declare a color palette
pal <- c("gray100", "gray90", "gray80", "gray70", "gray60",
"gray50", "gray40", "gray30", "gray20", "gray10")
# generate a random numbers dataset
x <- runif(n = 81, min = 0, max = 100)
X <- matrix(x, ncol = 9)
# create grid
png(file = "output/graph.png")
par(mar = c(1, 1, 1, 1) + 3.5)
# plot graph
plot(X, breaks = 10, col = pal)
# dev.off()

In the example above, the matrix has no structure because it has been constructed randomly, drawing its elements from a uniform distribution.

The diagonal matrix below uses a diagonal data structure instead:

Press + to interact
library(plot.matrix)
pal <- c("gray100", "gray90", "gray80", "gray70", "gray60",
"gray50", "gray40", "gray30", "gray20", "gray10")
X <- diag(runif(n = 9, min = 1, max = 10))
png(file = "output/graph.png")
par(mar = c(1, 1, 1, 1) + 3.5)
plot(X, breaks = 10, col = pal)
# dev.off()

In both of the example codes above:

  • Line 2: We’ve defined the color spectrum of the heatmap in pal.

  • Line 5: The runif() method is used to create random deviates of the uniform distribution.

  • Line 8: The png() method saves the output graph as graph.png and the plot() function displays the result.

  • Line 9: We use the par() function to define the margins.

Matrix assignment in Rcpp

In Rcpp, there are matrix classes for different data types:

  • LogicalMatrix
  • IntegerMatrix
  • NumericMatrix
  • ComplexMatrix
  • CharacterMatrix

Note: Throughout this course, we use the NumericMatrix class.


The next code example works similarly to the matrix function above. We need to do a little bit of work to implement byrow = TRUE because in Rcpp there’s no native assignment method by row. This lack of assignment is probably due to the fact that Rcpp is an interface between R and C++, and in C++, matrices are column-major. (This refers to how matrix data is stored in computers.)

Note: The main.cpp file contains the algorithm for the assignment operation. The main.r file implements the algorithm.

Press + to interact
main.r
main.cpp
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericMatrix mat_assign(NumericVector v, int rows, int cols, bool byrow = 0) {
if (!byrow) { // perform default matrix assignment if not byrow
NumericMatrix A(rows, cols, v.begin());
return(A);
} else { // loop over rows and cols to manually assign values if byrow
int k = 0;
NumericMatrix A(rows, cols);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
A(i,j) = v(k);
k++;
}
}
return(A);
}
}

Now, the Rcpp function can be tested in the same R use case by inserting matrix elements. First, we’ll insert elements by column.

A_cpp_col <- mat_assign(v = c(1, 2, 3, 4, 5, 6), rows = 2, cols = 3, byrow = 0)
Assigned by column

[135246]\begin{bmatrix} 1 & 3 & 5 \\ 2 & 4 & 6 \\ \end{bmatrix}


Then we will insert elements by row.

A_cpp_row <- mat_assign(v = c(1, 2, 3, 4, 5, 6), rows = 2, cols = 3, byrow = 1)
Assigned by row

[135246]\begin{bmatrix} 1 & 3 & 5 \\ 2 & 4 & 6 \\ \end{bmatrix}


Matrix assignment in Armadillo

Armadillo provides a matrix template class, but for the sake of simplicity, we’ll use typedef mat = Mat<double> throughout the course.

Armadillo has a matrix constructor that takes a vector and the requested matrix dimensions as inputs. Also, there’s no built-in method for assignment by row in Armadillo. Therefore, this case needs a little bit of additional work.

Press + to interact
main.r
main.cpp
#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]
using namespace Rcpp;
// [[Rcpp::export]]
// matrix assignment using the Armadillo library
arma::mat mat_assign_A(arma::vec v, int rows, int cols, bool byrow = 0) {
if (!byrow) {
arma::mat A(v.begin(), rows, cols);
return(A);
} else {
int k = 0;
arma::mat A(rows, cols);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
A(i,j) = v(k);
k++;
}
}
return(A);
}
}

Now, the mat_assign_A() function can be tested in the same R use case by inserting matrix elements. Again, first we’ll test by column:

A_arma_col <- mat_assign_A(c(1, 2, 3, 4, 5, 6), 2, 3, 0)

Note: Writing a matrix like:

c(1, 2, 3, 4, 5, 6), 2, 3, 0)

or:

c(1, 2, 3, 4, 5, 6), nrow = 2, ncol = 3, bycol = 0)

is a personal choice. Both statements mean that a (2×3)(2 \times 3) matrix is made.

Assigned with Armadillo by column

[135246]\begin{bmatrix} 1 & 3 & 5 \\ 2 & 4 & 6 \\ \end{bmatrix}


And next, we’ll test by row:

A_arma_row <- mat_assign_A(c(1, 2, 3, 4, 5, 6), 2, 3, 1)
Assigned with Armadillo by row

[123456]\begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ \end{bmatrix}


Matrix assignment in Eigen

Eigen provides a matrix template class, but to keep it simple, we’ve used the double data type class in this course with Dynamic dimensions defined as the following:

typedef Matrix<double, Dynamic, Dynamic> MatrixXd

Eigen uses an insertion operator («) to assign a vector to a matrix by column. In the assignment function implementation below, elementwise assignment is used to manage the by row case.

Press + to interact
main.r
main.cpp
#include <RcppEigen.h>
// [[Rcpp::depends(RcppEigen)]]
using namespace Rcpp;
using Eigen::MatrixXd;
using Eigen::VectorXd;
// [[Rcpp::export]]
// matrix assignment using the Eigen library
MatrixXd mat_assign_E(VectorXd v, int rows, int cols, bool byrow = 0) {
if (!byrow) {
MatrixXd A(rows, cols);
A << v;
return(A);
} else {
int k = 0;
MatrixXd A(rows, cols);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
A(i,j) = v(k);
k++;
}
}
return(A);
}
}

Now, we can test the Eigen function mat_assign_E() in the same R use case by inserting matrix elements. First, we’ll look at this by column:

A_Eigen_col <- mat_assign_E(c(1, 2, 3, 4, 5, 6), 2, 3, 0)
Assigned with Eigen by column

[135246]\begin{bmatrix} 1 & 3 & 5 \\ 2 & 4 & 6 \\ \end{bmatrix}


And then we’ll look at it again by row:

A_Eigen_row <- mat_assign_E(c(1, 2, 3, 4, 5, 6), 2, 3, 1)
Assigned with Eigen by row

[123456]\begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ \end{bmatrix}