R: R 矩阵与数组:二维与多维数据结构

最后更新:2026-08-26

上一课我们学了"一维"向量,这一课进入"二维"和"多维"——矩阵 (matrix)数组 (array)。矩阵是数据分析的基础(Excel 表格、图像像素、神经网络权重都是矩阵),这一课要把矩阵吃透。

矩阵的"灵魂"是 apply() 函数族——它让我们不用写循环就能按行/按列/按维度运算。这是 R 比 Python 简洁的地方。

1. 你将学到



2. 一个成绩单的故事

(1) 痛点:多科目成绩统计

小赵是教务员,5 个学生 3 门课的成绩单如下:

TEXT 📖 仅展示
       数学  语文  英语
Alice     85    78    92
Bob     72    88    80
Charlie     90    85    87
赵六     65    70    75
钱七     95    92    88

要算:① 每个学生的总分 ② 每科平均分 ③ 找出数学最高分的学生

如果用 Excel 公式要写半天;用 Python 写循环嵌套——

(2) R 的解法

R
# 5x3 矩阵
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("数学", "语文", "英语")
rownames(scores) <- c("Alice", "Bob", "Charlie", "赵六", "钱七")

# 1. 每个学生总分(一行求和)
row_sums <- apply(scores, 1, sum)

# 2. 每科平均(一列求平均)
col_means <- apply(scores, 2, mean)

# 3. 找出数学最高分
top_math <- which.max(scores[, "数学"])

5 行代码解决 3 个任务。这就是矩阵 + apply 的威力。



3. 矩阵的本质:二维向量

(1) 向量 → 矩阵的转换

100%
graph LR
    A["一维向量 c 1,2,3,4,5,6"] --> B["reshape 成 2x3 矩阵"]
    B --> C["1 2 3 / 4 5 6"]
    B --> D["1 4 / 2 5 / 3 6"]
    
    style A fill:#cce5ff
    style B fill:#fff3cd
R
# 一维向量
v <- 1:6
v
# [1] 1 2 3 4 5 6

# 转成 2 行 3 列矩阵
m <- matrix(v, nrow = 2, ncol = 3)
m
#      [,1] [,2] [,3]
# [1,]    1    3    5
# [2,]    2    4    6

(2) 矩阵的 4 个关键属性

属性 含义 示例
dim(m) 维度向量 dim(m)c(2, 3)
nrow(m) 行数 nrow(m)2
ncol(m) 列数 ncol(m)3
length(m) 总元素数 length(m)6
R
m <- matrix(1:6, nrow = 2)

dim(m)        # [1] 2 3
nrow(m)       # [1] 2
ncol(m)       # [1] 3
length(m)     # [1] 6  ← 总元素数


4. matrix():创建矩阵

(1) 基本语法

R
matrix(data, nrow = 1, ncol = 1, byrow = FALSE, dimnames = NULL)

(2) 4 种创建方式

R
# 1. 直接给数据 + 行列数
m1 <- matrix(1:6, nrow = 2)
#      [,1] [,2] [,3]
# [1,]    1    3    5   ← 默认按列填充
# [2,]    2    4    6

# 2. byrow = TRUE(按行填充)
m2 <- matrix(1:6, nrow = 2, byrow = TRUE)
#      [,1] [,2] [,3]
# [1,]    1    2    3
# [2,]    4    5    6

# 3. 加行列名
m3 <- matrix(1:6, nrow = 2, byrow = TRUE,
            dimnames = list(c("行1", "行2"), c("列A", "列B", "列C")))
#     列A 列B 列C
# 行1   1   2   3
# 行2   4   5   6

# 4. 只指定 nrow,ncol 自动推断
m4 <- matrix(1:6, nrow = 3)  # 3 行 2 列
#      [,1] [,2]
# [1,]    1    4
# [2,]    2    5
# [3,]    3    6
⚠️ 注意byrow = FALSE 是默认(按列填充)。多数场景下我们想要按行填充(一行是一个学生/一条记录),要显式写 byrow = TRUE

(3) 加行列名(更易读)

R
m <- matrix(1:6, nrow = 2, byrow = TRUE)
rownames(m) <- c("Alice", "Bob")
colnames(m) <- c("数学", "语文", "英语")
m
#     数学 语文 英语
# Alice   1    2    3
# Bob   4    5    6


5. 访问矩阵元素

(1) 3 种访问方式

方式 语法 用途
[i, j] 第 i 行第 j 列 单个元素
[i, ] 第 i 行 整行
[, j] 第 j 列 整列
[i, "列名"] 第 i 行的某列 按名字
R
m <- matrix(1:9, nrow = 3, byrow = TRUE,
            dimnames = list(c("Alice", "Bob", "Charlie"),
                          c("数学", "语文", "英语")))
m
#     数学 语文 英语
# Alice   1    2    3
# Bob   4    5    6
# Charlie   7    8    9

# 单个元素
m[2, 3]            # [1] 6
m["Bob", "英语"]  # [1] 6

# 整行
m[1, ]             # 数学 1 语文 2 英语 3
m["Alice", ]        # 同上

# 整列
m[, 1]             # Alice 1 Bob 4 Charlie 7
m[, "数学"]        # 同上

(2) 逻辑索引

R
# 找出数学成绩 > 5 的学生
m[m[, "数学"] > 5, ]
#     数学 语文 英语
# Bob   4 ...   ← 不会被选中
# Charlie   7    8    9


6. rbind/cbind:合并矩阵

100%
graph TB
    A["矩阵 A 3x2"] --> C["rbind 按行拼接"]
    B["矩阵 B 2x2"] --> C
    C --> D["新矩阵 5x2"]
    
    E["矩阵 A 3x2"] --> F["cbind 按列拼接"]
    G["矩阵 B 3x2"] --> F
    F --> H["新矩阵 3x4"]
    
    style A fill:#cce5ff
    style B fill:#cce5ff
    style E fill:#d4edda
    style G fill:#d4edda
R
a <- matrix(1:6, nrow = 3, byrow = TRUE)
b <- matrix(7:12, nrow = 3, byrow = TRUE)

# 按行拼接(行数相加)
rbind(a, b)
#      [,1] [,2]
# [1,]    1    2
# [2,]    3    4
# [3,]    5    6
# [4,]    7    8
# [5,]    9   10
# [6,]   11   12

# 按列拼接(列数相加)
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) 要求 ncol(a) == ncol(b)cbind(a, b) 要求 nrow(a) == nrow(b),否则报错。



7. apply 系列函数(灵魂)

(1) 为什么需要 apply?

R 的循环很慢,apply 让我们不写循环就能按行/按列运算:

R
m <- matrix(1:9, nrow = 3)

# 求每行和(不用循环)
apply(m, 1, sum)
# [1] 12 15 18

# 求每列和
apply(m, 2, sum)
# [1] 12 15 18

(2) apply() 核心

R
apply(X, MARGIN, FUN)
参数 含义 取值
X 矩阵/数组 matrix / array
MARGIN 维度 1=行, 2=列, c(1,2)=行列
FUN 要应用的函数 sum mean max function(x) ...
R
# 1=行(按行处理),2=列(按列处理)
apply(m, 1, mean)   # 每行平均
apply(m, 2, max)    # 每列最大

# 自定义函数
apply(m, 1, function(x) max(x) - min(x))  # 每行极差

(3) apply 家族 5 个函数

函数 输入 输出 典型用途
apply() 矩阵/数组 向量/矩阵 按行/列运算
lapply() 列表/向量 列表 列表处理
sapply() 列表/向量 向量/矩阵 简化 lapply
tapply() 向量+分组 数组 分组统计
mapply() 多向量 列表/向量 多参数函数

(4) lapply() vs sapply()

R
# lapply 返回列表
numbers <- list(a = 1:3, b = 4:6, c = 7:9)
result_l <- lapply(numbers, sum)
# $a 6
# $b 15
# $c 24

# sapply 返回向量(更常用)
result_s <- sapply(numbers, sum)
#  a  b  c
#  6 15 24
💡 提示优先用 sapply()——返回向量更方便。lapply() 在函数式编程管道中更常用。

(5) tapply() 分组统计

R
# 5 个学生 3 门课成绩,按"性别"分组
scores <- c(85, 78, 92, 72, 88, 80, 90, 85, 87, 65, 70, 75, 95, 92, 88)
gender <- c("男", "女", "男", "男", "女", "女", "男", "男", "女", "男", "女", "男", "女", "男", "女")

# 按性别分组求平均
tapply(scores, gender, mean)
#   女   男 
# 84.6 81.5
💡 提示tapply() 在做"分组统计"时极其常用——但下一阶段用 dplyr::group_by() | summarise() 会更优雅。

(6) mapply() 多参数函数

R
# 对两个向量对应元素求和
mapply(function(a, b) a + b, 1:3, 10:12)
# [1] 11 13 15


8. 数组 array:多维扩展

R
# 2x3x4 的三维数组
arr <- array(1:24, dim = c(2, 3, 4))

# 第 1 个 2x3 矩阵
arr[, , 1]
#      [,1] [,2] [,3]
# [1,]    1    3    5
# [2,]    2    4    6

# apply 可以对多维运算
apply(arr, c(1, 2), mean)  # 对第 3 维求平均
💡 提示实际开发中数组用得少——data.frame(下一阶段)和 array(图像/物理)才是多维数据的主流。



9. 矩阵运算(线性代数)

运算 语法 说明
转置 t(m) 行变列
矩阵乘法 m1 %*% m2 线性代数乘法
元素乘法 m1 * m2 对应元素乘
求逆 solve(m) 逆矩阵
求和(行/列) rowSums(m) / colSums(m) 快速求和
R
m <- matrix(1:4, nrow = 2)
#      [,1] [,2]
# [1,]    1    3
# [2,]    2    4

# 转置
t(m)
#      [,1] [,2]
# [1,]    1    2
# [2,]    3    4

# 矩阵乘法(用 m %*% m)
m %*% m
#      [,1] [,2]
# [1,]    7   15
# [2,]   10   22

# 求逆
solve(m)
#      [,1] [,2]
# [1,]   -2  1.5
# [2,]    1 -0.5


10. 完整示例:成绩单综合分析

下面是一个完整工作流示例,把本课所有矩阵 + apply 串起来。

▶ 示例:班级成绩综合分析

R 📖 仅展示
# ============================================
# 班级成绩综合分析
# 功能:5 学生 3 课程的成绩矩阵完整分析
# ============================================

# 1. 创建成绩矩阵(按行填充:每个学生一行)
scores <- matrix(c(
  85, 78, 92,   # Alice
  72, 88, 80,   # Bob
  90, 85, 87,   # Charlie
  65, 70, 75,   # 赵六
  95, 92, 88    # 钱七
), nrow = 5, byrow = TRUE)

# 加行列名
rownames(scores) <- c("Alice", "Bob", "Charlie", "赵六", "钱七")
colnames(scores) <- c("数学", "语文", "英语")

cat("=== 成绩矩阵 ===\n")
print(scores)

# 2. 每个学生总分(按行求和)
cat("\n=== 学生总分 ===\n")
row_totals <- apply(scores, 1, sum)
print(row_totals)

# 3. 每科平均/最高/最低(按列统计)
cat("\n=== 各科统计 ===\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. 每个学生平均分(按行求平均)
cat("\n=== 学生平均分 ===\n")
row_means <- apply(scores, 1, mean)
print(round(row_means, 2))

# 5. 找出数学最高分学生
top_math <- which.max(scores[, "数学"])
cat("\n数学最高分:", rownames(scores)[top_math], "(", max(scores[, "数学"]), "分)\n")

# 6. 找出总分最高学生
top_total <- which.max(row_totals)
cat("总分最高:", rownames(scores)[top_total], "(", max(row_totals), "分)\n")

# 7. 找出单科 < 70 的学生(用逻辑索引)
fail <- scores < 70
cat("\n不及格科目(< 70):\n")
print(scores)
cat("\n是否不及格(TRUE = 不及格):\n")
print(fail)

# 8. 综合排名
cat("\n=== 综合排名(按总分降序)===\n")
ranking <- order(row_totals, decreasing = TRUE)
result <- data.frame(
  排名 = 1:5,
  姓名 = rownames(scores)[ranking],
  总分 = row_totals[ranking],
  平均 = round(row_totals[ranking] / 3, 2)
)
print(result)
逻辑代码 43 行(超过 40 行限制,仅展示)

预期输出(节选):

TEXT 📖 仅展示
=== 成绩矩阵 ===
    数学 语文 英语
Alice   85   78   92
Bob   72   88   80
Charlie   90   85   87
赵六   65   70   75
钱七   95   92   88

=== 学生总分 ===
Alice Bob Charlie 赵六 钱七
255  240  262  210  275

=== 学生平均分 ===
Alice   Bob   Charlie   赵六   钱七
85.00  80.00  87.33  70.00  91.67

=== 综合排名(按总分降序)===
  排名 姓名 总分 平均
1   1 钱七  275 91.67
2   2 Charlie  262 87.33
3   3 Alice  255 85.00
4   4 Bob  240 80.00
5   5 赵六  210 70.00

❓ 常见问题

Q byrow = TRUE 和 FALSE 区别?
A matrix(1:6, nrow=2, byrow=FALSE)(默认)按列填充:1 3 5 / 2 4 6byrow=TRUE 按行填充:1 2 3 / 4 5 6。数据科学场景几乎都按行填(每个样本一行)。
Q apply() 和 for 循环怎么选?
A 能用 apply() 就用 apply()——R 循环慢 10-100 倍。apply(m, 1, fun) 替代"按行循环",apply(m, 2, fun) 替代"按列循环"。
Q tapply() 在实际项目中怎么用?
A tapply(x, group, fun) 快速做"分组统计"——按性别、地区、月份等分组。但下一阶段用 dplyr::group_by() \| summarise() 更优雅,tapply 是基础。

📖 小节


📝 作业

  1. 基础题:创建一个 4×4 矩阵,元素为 1-16,按行填充,行列名分别为 c("A","B","C","D")c("X","Y","Z","W")。打印矩阵,验证行列名正确。

  2. 基础题:用 apply() 计算上题矩阵的每行和、每列和、每行平均、每列最大。

  3. 基础题:用 rbind()cbind() 合并两个 3×3 矩阵(自己构造),验证行列数变化。

  4. 进阶题:模拟 6 个学生 4 门课成绩:① 用 apply() 计算每个学生总分;② 用 apply() 计算每科平均分;③ 用 which.max() 找出每科最高分学生姓名;④ 用 order() 排名。

  5. 挑战题:写一段脚本,对 100 个学生 5 门课成绩做"分层次分析":① 用 tapply() 按"性别"分组计算总平均;② 找出数学 90 分以上、英语不及格(< 60)的学生数量;③ 计算每个学生 5 门课的标准差(用 apply() + 自定义函数)。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏