R: R Matrices and Arrays
Last updated: 2026-08-26
In the previous lesson, we learned about "one-dimensional" vectors. In this lesson, we’ll move on to "two-dimensional" and "multidimensional" concepts—matrices and arrays. Matrices are the foundation of data analysis (Excel spreadsheets, image pixels, and neural network weights are all matrices), so let’s make sure we fully understand them in this lesson.
The "heart" of matrices is the apply() function family—it allows us to perform operations by row, column, or dimension without writing loops. This is where R is more concise than Python.
1. What You'll Learn
- matrix() creates a matrix (filled by rows or columns)
- Matrix properties (dim, nrow, ncol, rownames, colnames)
- rbind/cbind: Merging Matrices
- apply family functions (apply/lapply/sapply/tapply/mapply)
- Multidimensional Extensions of the
arrayData Type - Matrix Operations (Transpose
%*%, Inversesolve()) - Hands-On: Transcript Statistics
2. The Story of a Transcript
(1) Pain Point: Compiling Grades Across Multiple Subjects
Xiao Zhao is an academic affairs officer. The transcripts for five students in three courses are as follows:
Mathematics Chinese Language English
Alice 85 78 92
Bob 72 88 80
Charlie 90 85 87
Diana 65 70 75
Eve 95 92 88
Calculate: ① Each student’s total score; ② The average score for each subject; ③ Identify the student with the highest score in math.
If you were to write this using Excel formulas, it would take forever; if you were to write it in Python, you’d have to use nested loops—
(2) Solution using R
# 5x3 Matrix
scores <- matrix(c(85, 78, 92,
72, 88, 80,
90, 85, 87,
65, 70, 75,
95, 92, 88),
nrow = 5, byrow = TRUE)
colnames(scores) <- c("Mathematics", "Chinese Language", "English")
rownames(scores) <- c("Alice", "Bob", "Charlie", "Diana", "Eve")
# 1. Each Student's Total Score(Sum in One Line)
row_sums <- apply(scores, 1, sum)
# 2. Average per subject(Calculate the average for a column)
col_means <- apply(scores, 2, mean)
# 3. Find the highest score in math
top_math <- which.max(scores[, "Mathematics"])
Five lines of code to solve three tasks. That’s the power of matrices and the apply function.
3. The Essence of Matrices: Two-Dimensional Vectors
(1) Conversion from Vectors to Matrices
graph LR
A["One-dimensional vector c 1,2,3,4,5,6"] --> B["reshape into 2x3 Matrix"]
B --> C["1 2 3 / 4 5 6"]
B --> D["1 4 / 2 5 / 3 6"]
style A fill:#cce5ff
style B fill:#fff3cd
# One-dimensional vector
v <- 1:6
v
# [1] 1 2 3 4 5 6
# Convert to 2 row 3 List of Matrices
m <- matrix(v, nrow = 2, ncol = 3)
m
# [,1] [,2] [,3]
# [1,] 1 3 5
# [2,] 2 4 6
(2) The Four Key Properties of Matrices
| Property | Meaning | Example |
|---|---|---|
dim(m) |
dimensional vector | dim(m) → c(2, 3) |
nrow(m) |
Line number | nrow(m) → 2 |
ncol(m) |
Number of rows | ncol(m) → 3 |
length(m) |
Total number of elements | length(m) → 6 |
m <- matrix(1:6, nrow = 2)
dim(m) # [1] 2 3
nrow(m) # [1] 2
ncol(m) # [1] 3
length(m) # [1] 6 ← Total Number of Elements
4. matrix(): Create a matrix
(1) Basic Syntax
matrix(data, nrow = 1, ncol = 1, byrow = FALSE, dimnames = NULL)
(2) 4 Ways to Create
# 1. Provide the data directly + Number of rows and columns
m1 <- matrix(1:6, nrow = 2)
# [,1] [,2] [,3]
# [1,] 1 3 5 ← Fill by column by default
# [2,] 2 4 6
# 2. byrow = TRUE(Fill by Row)
m2 <- matrix(1:6, nrow = 2, byrow = TRUE)
# [,1] [,2] [,3]
# [1,] 1 2 3
# [2,] 4 5 6
# 3. Add row and column names
m3 <- matrix(1:6, nrow = 2, byrow = TRUE,
dimnames = list(c("row1", "row2"), c("colA", "colB", "colC")))
# colA colB colC
# row1 1 2 3
# row2 4 5 6
# 4. Specify only nrow,ncol Automatic Inference
m4 <- matrix(1:6, nrow = 3) # 3 row 2 col
# [,1] [,2]
# [1,] 1 4
# [2,] 2 5
# [3,] 3 6
byrow = FALSE is the default (fill by column). In most cases, we want to fill by row (one student per row/one record), so you must explicitly specify byrow = TRUE.
(3) Add column and row labels (for better readability)
m <- matrix(1:6, nrow = 2, byrow = TRUE)
rownames(m) <- c("Alice", "Bob")
colnames(m) <- c("Mathematics", "Chinese Language", "English")
m
# Mathematics Chinese Language English
# Alice 1 2 3
# Bob 4 5 6
5. Accessing Matrix Elements
(1) 3 Ways to Access
| Method | Syntax | Purpose |
|---|---|---|
[i, j] |
Row i, Column j | Single element |
[i, ] |
Line i | Entire line |
[, j] |
Column j | Entire column |
[i, "col_name"] |
A column in row i | By name |
m <- matrix(1:9, nrow = 3, byrow = TRUE,
dimnames = list(c("Alice", "Bob", "Charlie"),
c("Mathematics", "Chinese Language", "English")))
m
# Mathematics Chinese Language English
# Alice 1 2 3
# Bob 4 5 6
# Charlie 7 8 9
# A single element
m[2, 3] # [1] 6
m["Bob", "English"] # [1] 6
# Entire row
m[1, ] # Mathematics 1 Chinese Language 2 English 3
m["Alice", ] # Ibid.
# Align
m[, 1] # Alice 1 Bob 4 Charlie 7
m[, "Mathematics"] # Ibid.
(2) Logical Index
# Find the math scores > 5 students
m[m[, "Mathematics"] > 5, ]
# Mathematics Chinese Language English
# Bob 4 ... ← Will not be selected
# Charlie 7 8 9
6. rbind/cbind: Merging Matrices
graph TB
A["Matrix A 3x2"] --> C["rbind Concatenate by row"]
B["Matrix B 2x2"] --> C
C --> D["New Matrix 5x2"]
E["Matrix A 3x2"] --> F["cbind Concatenate by Column"]
G["Matrix B 3x2"] --> F
F --> H["New Matrix 3x4"]
style A fill:#cce5ff
style B fill:#cce5ff
style E fill:#d4edda
style G fill:#d4edda
a <- matrix(1:6, nrow = 3, byrow = TRUE)
b <- matrix(7:12, nrow = 3, byrow = TRUE)
# Concatenate by row(Sum of Rows)
rbind(a, b)
# [,1] [,2]
# [1,] 1 2
# [2,] 3 4
# [3,] 5 6
# [4,] 7 8
# [5,] 9 10
# [6,] 11 12
# Concatenate by Column(Sum of a Series)
cbind(a, b)
# [,1] [,2] [,3] [,4]
# [1,] 1 2 7 8
# [2,] 3 4 9 10
# [3,] 5 6 11 12
rbind(a, b) requires ncol(a) == ncol(b); cbind(a, b) requires nrow(a) == nrow(b), otherwise an error will occur.
7. The apply Series of Functions (The Essence)
(1) Why is apply needed?
Loops in R are very slow, but apply lets us perform operations row-by-row or column-by-column without writing loops:
m <- matrix(1:9, nrow = 3)
# Find the sum of each row(No need for a loop)
apply(m, 1, sum)
# [1] 12 15 18
# Find the sum of each column
apply(m, 2, sum)
# [1] 12 15 18
(2) The Core of apply()
apply(X, MARGIN, FUN)
| Parameter | Meaning | Possible Values |
|---|---|---|
X |
Matrix/Array | matrix / array |
MARGIN |
Dimension | 1=row, 2=column, c(1,2)=row and column |
FUN |
Function to be applied | sum mean max function(x) ... |
# 1=row(Process by row),2=col(Process by Column)
apply(m, 1, mean) # Average per line
apply(m, 2, max) # Maximum per column
# Custom Functions
apply(m, 1, function(x) max(x) - min(x)) # Range per row
(3) The 5 functions in the apply family
| Function | Input | Output | Typical Uses |
|---|---|---|---|
apply() |
Matrices/Arrays | Vectors/Matrices | Row/Column Operations |
lapply() |
Lists/Vectors | Lists | List Processing |
sapply() |
Lists/Vectors | Vectors/Matrices | Simplified lapply |
tapply() |
Vectors + Groups | Arrays | Group Statistics |
mapply() |
Multivector | List/Vector | Multi-argument function |
(4) lapply() vs sapply()
# lapply Back to List
numbers <- list(a = 1:3, b = 4:6, c = 7:9)
result_l <- lapply(numbers, sum)
# $a 6
# $b 15
# $c 24
# sapply Return Vector(More commonly used)
result_s <- sapply(numbers, sum)
# a b c
# 6 15 24
sapply() first—it’s more convenient for returning vectors. lapply() is more commonly used in functional programming pipelines.
(5) tapply() Grouped Statistics
# 5 Students 3 Course Grades, grouped by "Gender"
scores <- c(85, 78, 92, 72, 88, 80, 90, 85, 87, 65, 70, 75, 95, 92, 88)
gender <- c("M", "F", "M", "M", "F", "F", "M", "M", "F", "M", "F", "M", "F", "M", "F")
# Calculate the average by gender
tapply(scores, gender, mean)
# F M
# 84.6 81.5
tapply() is extremely commonly used when performing "group statistics"—but using dplyr::group_by() | summarise() in the next phase would be more elegant.
(6) mapply()—A Multi-Argument Function
# Sum the corresponding elements of two vectors
mapply(function(a, b) a + b, 1:3, 10:12)
# [1] 11 13 15
8. Arrays: Multidimensional Extensions
# 2x3x4 a three-dimensional array
arr <- array(1:24, dim = c(2, 3, 4))
# 1st 2x3 Matrix
arr[, , 1]
# [,1] [,2] [,3]
# [1,] 1 3 5
# [2,] 2 4 6
# apply Supports multidimensional operations
apply(arr, c(1, 2), mean) # Regarding No. 3 Find the average
data.frame (Next Stage) and array (Images/Physics) are the mainstream approaches for multidimensional data.
9. Matrix Operations (Linear Algebra)
| Operation | Syntax | Description |
|---|---|---|
| Transpose | t(m) |
Row-to-Column |
| Matrix Multiplication | m1 %*% m2 |
Linear Algebra Multiplication |
| Element-wise Multiplication | m1 * m2 |
Corresponding Element-wise Multiplication |
| Inverse | solve(m) |
Inverse Matrix |
| Sum (Rows/Columns) | rowSums(m) / colSums(m) |
Quick Sum |
m <- matrix(1:4, nrow = 2)
# [,1] [,2]
# [1,] 1 3
# [2,] 2 4
# Transpose
t(m)
# [,1] [,2]
# [1,] 1 2
# [2,] 3 4
# Matrix Multiplication (use m %*% m)
m %*% m
# [,1] [,2]
# [1,] 7 15
# [2,] 10 22
# Inverse
solve(m)
# [,1] [,2]
# [1,] -2 1.5
# [2,] 1 -0.5
10. Complete Example: Comprehensive Analysis of a Transcript
Below is an example of a complete workflow that combines all the matrices and apply functions from this lesson.
▶ Example: Comprehensive Analysis of Class Grades
# ============================================
# Comprehensive Analysis of Class Performance
# Features:5 Student 3 A Comprehensive Analysis of the Course Grade Matrix
# ============================================
# 1. Create a Grade Matrix(Fill by Row:One student per line)
scores <- matrix(c(
85, 78, 92, # Alice
72, 88, 80, # Bob
90, 85, 87, # Charlie
65, 70, 75, # Diana
95, 92, 88 # Eve
), nrow = 5, byrow = TRUE)
# Add row and column names
rownames(scores) <- c("Alice", "Bob", "Charlie", "Diana", "Eve")
colnames(scores) <- c("Mathematics", "Chinese Language", "English")
cat("=== Grade Matrix ===\n")
print(scores)
# 2. Each Student's Total Score(Sum by Row)
cat("\n=== Student's Total Score ===\n")
row_totals <- apply(scores, 1, sum)
print(row_totals)
# 3. Average per subject/Highest/Lowest(Tally by Column)
cat("\n=== Statistics by Subject ===\n")
col_stats <- apply(scores, 2, function(x) {
c(mean = round(mean(x), 2),
max = max(x),
min = min(x),
sd = round(sd(x), 2))
})
print(col_stats)
# 4. Average score per student(Calculate the average by row)
cat("\n=== Average Student Score ===\n")
row_means <- apply(scores, 1, mean)
print(round(row_means, 2))
# 5. Identify the student with the highest math score
top_math <- which.max(scores[, "Mathematics"])
cat("\nHighest Score in Math:", rownames(scores)[top_math], "(", max(scores[, "Mathematics"]), "min)\n")
# 6. Identify the student with the highest total score
top_total <- which.max(row_totals)
cat("Highest Total Score:", rownames(scores)[top_total], "(", max(row_totals), "min)\n")
# 7. Find a single subject < 70 students(Using Logical Indexes)
fail <- scores < 70
cat("\nFailed Courses(< 70):\n")
print(scores)
cat("\nDid I fail?(TRUE = Fail):\n")
print(fail)
# 8. Overall Ranking
cat("\n=== Overall Ranking(Sort by total score in descending order)===\n")
ranking <- order(row_totals, decreasing = TRUE)
result <- data.frame(
Ranking = 1:5,
Name = rownames(scores)[ranking],
Total Score = row_totals[ranking],
Average = round(row_totals[ranking] / 3, 2)
)
print(result)
Expected Output (Excerpt):
=== Grade Matrix ===
Mathematics Chinese Language English
Alice 85 78 92
Bob 72 88 80
Charlie 90 85 87
Diana 65 70 75
Eve 95 92 88
=== Student's Total Score ===
Alice Bob Charlie Diana Eve
255 240 262 210 275
=== Average Student Score ===
Alice Bob Charlie Diana Eve
85.00 80.00 87.33 70.00 91.67
=== Overall Ranking(Sort by total score in descending order)===
Ranking Name Total Score Average
1 1 Eve 275 91.67
2 2 Charlie 262 87.33
3 3 Alice 255 85.00
4 4 Bob 240 80.00
5 5 Diana 210 70.00
❓ FAQ
byrow = TRUE and FALSE?matrix(1:6, nrow=2, byrow=FALSE) (default) fills by column: 1 3 5 / 2 4 6; byrow=TRUE fills by row: 1 2 3 / 4 5 6. In data science scenarios, data is almost always filled by row (one row per sample).apply() and a for loop?apply() whenever possible—R loops are 10 to 100 times slower. Use apply(m, 1, fun) instead of "looping by row," and apply(m, 2, fun) instead of "looping by column."lapply() and sapply()?lapply returns a list, while sapply simplifies the result to a vector or matrix whenever possible. For everyday use, sapply is sufficient (the result is more concise).%*% and *?%*% is matrix multiplication (following the rules of linear algebra), while * is element-wise multiplication (multiplying corresponding elements). Linear regression, neural networks, and other applications require %*%.tapply() How is this used in real-world projects?tapply(x, group, fun) To quickly perform "grouped statistics"—grouping by gender, region, month, etc. However, in the next phase, using dplyr::group_by() \| summarise() is a more elegant approach; tapply serves as the foundation.📖 Summary
- A matrix is a two-dimensional vector, while an array is a multidimensional vector;
matrix()creation,array()extension to multiple dimensions byrow = TRUEFill by row (data science standard),FALSE(default) Fill by column- Access method
m[i, j]/m[i, ]/m[, j]/m["row", "col"] rbind()Sort by row,cbind()Sort by column- apply family:
apply()By Dimension,lapply()List → List,sapply()List → Vector,tapply()Grouped Statistics,mapply()Multiple Parameters - Matrix Operations:
t()Transpose,%*%Matrix Multiplication,solve()Matrix Inverse,rowSums()colSums()Fast Summation - Use
applyinstead of loops: Loops in R are 10–100 times slower; if you can useapply, don’t write loops
📝 Exercises
-
Basic Problem: Create a 4×4 matrix with elements ranging from 1 to 16, filled row by row, with row and column labels
c("A","B","C","D")andc("X","Y","Z","W"), respectively. Print the matrix and verify that the row and column labels are correct. -
Basic Problem: Use
apply()to calculate the row sums, column sums, row averages, and column maximums for the matrix from the previous problem. -
Basic Exercise: Use
rbind()andcbind()to merge two 3×3 matrices (which you construct yourself), and verify how the number of rows and columns changes. -
Advanced Problem: Simulate the grades of 6 students in 4 courses: ① Use
apply()to calculate each student’s total score; ② Useapply()to calculate the average score for each course; ③ Usewhich.max()to find the name of the student with the highest score in each course; ④ Useorder()to rank the students. -
Challenge: Write a script to perform a "tiered analysis" of the grades for 100 students across 5 courses: ① Use
tapply()to calculate the overall average by grouping by "gender"; ② Determine the number of students who scored 90 or higher in math but failed English (scored < 60); ③ Calculate the standard deviation for each student’s 5 courses (usingapply()+ a custom function).