flowchart LR
B["Build <br> matrix() / rbind() / cbind()"] --> M["A Matrix"]
M --> I["Index <br> [i, j], negative, logical"]
M --> AR["Arithmetic <br> element-wise + scalar recycling"]
M --> LA["Linear Algebra <br> t() %*% det() solve()"]
M --> AP["Row/Column summaries <br> rowSums() apply()"]
style B fill:#e3f2fd,stroke:#1976D2
style M fill:#fff3e0,stroke:#F57C00
style I fill:#e8f5e9,stroke:#388E3C
style AR fill:#f3e5f5,stroke:#8E24AA
style LA fill:#f3e5f5,stroke:#8E24AA
style AP fill:#e8f5e9,stroke:#388E3C
14 Matrices in R
This chapter is a complete tour of the matrix, R’s two-dimensional, single-type container. You will learn three ways to build one (matrix(), rbind(), cbind()), how rows and columns are stored and named, the four styles of indexing ([i, j], single-bracket, negative, logical), and how to update cells or whole rows and columns. You will see how arithmetic and the summary functions rowSums(), colSums(), rowMeans(), and colMeans() treat matrices, and the handful of linear-algebra operators that matter for applied work: transpose t(), matrix multiplication %*%, the determinant det(), and the matrix inverse solve(). You will also meet apply(), R’s apply-family entry for 2-D data. By the end of this chapter you will be able to build a matrix for any rectangular, single-type problem and manipulate it with confidence.
14.1 What a Matrix Is
A matrix is a vector whose elements are laid out in a rectangle of rows and columns. All cells share one atomic type, which is what distinguishes a matrix from a data frame. Internally, R stores a matrix as a single vector with a dim attribute (c(nrow, ncol)), filled by column by default.
| Property | Matrix | Data Frame |
|---|---|---|
| Number of dimensions | 2 | 2 |
| All columns one type? | Yes. | No. |
| Typical use | Numeric grids, images, distances, correlations. | Survey-style tabular data. |
| Core functions | matrix(), %*%, t(), solve(). |
data.frame(), dplyr verbs. |
14.2 Building a Matrix
matrix(): the Direct Constructor
rbind() and cbind(): Build from Vectors
rbind() stacks vectors as rows; cbind() stacks them as columns. Both functions coerce to a matrix if all inputs share a type.
Like vector names, row and column names are accessed and set with rownames() and colnames() (or with the single-object dimnames()).
14.3 Indexing a Matrix
[row, col] is the Core Pattern
When you select a single row or column, R collapses the result to a plain vector by default. Pass drop = FALSE to keep a matrix.
m[1] selects the first element treating the matrix as a flat vector; m[1, ] selects the whole first row. The comma is not optional.
14.4 Modifying a Matrix
Use rbind() to append a new row and cbind() to append a new column.
14.5 Arithmetic on Matrices
Arithmetic operators work cell by cell, exactly as they do on vectors. Scalars are recycled. Two matrices of the same shape combine element by element.
* is Element-Wise; %*% is Matrix Multiplication
A common slip from linear-algebra textbooks is to expect A * B to perform matrix multiplication. In R, * is element-wise. Use %*% for the standard row-by-column product.
14.6 Summarising Rows and Columns
| Function | Returns |
|---|---|
rowSums(m), colSums(m) |
One sum per row or per column. |
rowMeans(m), colMeans(m) |
One mean per row or per column. |
apply(m, 1, FUN) |
Apply FUN to each row. |
apply(m, 2, FUN) |
Apply FUN to each column. |
rowSums() and colSums() are written in C and can be an order of magnitude faster than apply(m, 1, sum). Use them when they exist; reach for apply() for custom functions like sd or an anonymous summary.
14.7 Linear-Algebra Essentials
| Operator / Function | Does |
|---|---|
t(A) |
Transpose. |
A %*% B |
Matrix multiplication. |
det(A) |
Determinant (square matrices). |
solve(A) |
Matrix inverse (for non-singular square A). solve(A, b) solves A x = b. |
solve(A) on a Singular Matrix
If det(A) == 0, the matrix is singular and has no inverse. solve() will error. Always check det() (or use solve(A, b) instead of computing the full inverse) for numerical stability.
14.8 Reshaping and Combining
dim()<- for Quick Reshapes
Assigning to dim() reshapes an existing vector or matrix. The total number of cells must stay the same.
14.9 A Worked Example: A Monthly Sales Matrix
Every technique from the chapter is on display: construction with matrix(), named dimnames, index-based extraction, element-wise arithmetic for growth, rowSums() / colSums() for summaries, and cbind() / rbind() to append totals.
14.10 Summary
| Concept | Key Takeaway |
|---|---|
| Structure | Rectangular, single-type; a vector with a dim attribute. |
| Build | matrix() for direct construction; rbind() / cbind() for assembly. |
| Fill order | Column-major by default; use byrow = TRUE to fill by row. |
| Naming | rownames(), colnames(), or dimnames(). |
| Indexing | [i, j] is the key form; remember the comma and drop = FALSE. |
| Arithmetic | Element-wise by default; use %*% for matrix multiplication. |
| Summaries | rowSums(), colSums(), rowMeans(), colMeans(), and apply(). |
| Linear algebra | t(), %*%, det(), solve() cover most applied needs. |
| Data frame vs matrix | Same shape, different contents, use a matrix when every column shares a type. |
Matrices are the natural home of numeric data that fits in a rectangle: correlation structures, distance tables, monthly-regional dashboards, image pixels. When all your columns share a type and you want fast numeric work, a matrix is the right choice over a data frame. In the next chapter you will meet arrays, the n-dimensional cousin of matrices, used when two dimensions are not enough.