Assignment Operations on Matrices
Learn how to assign matrices using R, Rcpp, Armadillo, and Eigen libraries.
What is a matrix?
Matrix of dimension is an collection of elements. The elements are ordered according to a rectangular scheme consisting of rows and columns, such as:
Here, , with and .
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.
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 . -
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()
andncol()
functions print the number of rows and the number of columns in the matrix, respectively.
The returned object is of class matrix
of dimension , that is, rows and columns.
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.
library(plot.matrix)# declare a color palettepal <- c("gray100", "gray90", "gray80", "gray70", "gray60","gray50", "gray40", "gray30", "gray20", "gray10")# generate a random numbers datasetx <- runif(n = 81, min = 0, max = 100)X <- matrix(x, ncol = 9)# create gridpng(file = "output/graph.png")par(mar = c(1, 1, 1, 1) + 3.5)# plot graphplot(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:
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 asgraph.png
and theplot()
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. Themain.r
file implements the algorithm.
#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 byrowNumericMatrix A(rows, cols, v.begin());return(A);} else { // loop over rows and cols to manually assign values if byrowint 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)
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)
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.
#include <RcppArmadillo.h>// [[Rcpp::depends(RcppArmadillo)]]using namespace Rcpp;// [[Rcpp::export]]// matrix assignment using the Armadillo libraryarma::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 matrix is made.
And next, we’ll test by row:
A_arma_row <- mat_assign_A(c(1, 2, 3, 4, 5, 6), 2, 3, 1)
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.
#include <RcppEigen.h>// [[Rcpp::depends(RcppEigen)]]using namespace Rcpp;using Eigen::MatrixXd;using Eigen::VectorXd;// [[Rcpp::export]]// matrix assignment using the Eigen libraryMatrixXd 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)
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)