R: R 向量 (Vector):R 编程的核心数据结构
最后更新:2026-08-26
上一课我们学了 R 的 6 种数据类型,这一课开始学 R 的核心数据结构——向量。在 Python 里写
1, 2, 3得到的是三个独立数字,要装进列表得写[1, 2, 3]。但 R 不一样——R 里1, 2, 3直接就是向量。
R 的向量是整个语言的基石:数字、字符串、逻辑值都是向量。这一课我们要把向量吃透,因为它会出现在后续每一节课的代码里。
1. 你将学到
- 什么是 R 向量,为什么它是 R 的核心
- 4 种创建向量的方法(c() / : / seq() / rep())
- 5 种访问向量元素的方式(正/负整数、逻辑、命名)
- 向量化的算术运算(标量 + 向量、向量 + 向量)
- 9 个常用聚合函数(sum、mean、sd 等)
- 排序、去重、查找的常用操作
2. 一个学生成绩管理的故事
(1) 任务背景
Ivan是the class teacher,学期末要统计全班 30 个学生的成绩。他需要做这些事:
- 计算平均分、最高分、及格率
- 找出 90 分以上的学生
- 把成绩从高到低排序
- 给每个分数评级(优秀/良好/及格/不及格)
如果用 Excel,他得反复复制粘贴 + 透视表。如果用 Python,他得写列表、循环、列表推导式。但用 R——
(2) R 的解法
# 一行代码生成 30 个学生成绩(从 50 到 100 随机)
set.seed(42)
scores <- sample(50:100, 30, replace = TRUE)
# 一行代码完成所有统计
mean(scores) # 平均分
max(scores) # 最高分
sum(scores >= 60) / length(scores) # 及格率
scores[scores >= 90] # 90 分以上
sort(scores, decreasing = TRUE) # 降序排序
10 行代码解决 5 个任务。这就是 R 向量的魅力——用最少的代码处理一列数据。
3. 什么是向量?
向量(vector)是 R 里最基本的数据结构,是一组同类型元素的有序集合。
(1) 向量的本质
graph LR
A["R 向量"] --> B["原子性<br/>单个数字 = 长度1的向量"]
A --> C["同质性<br/>所有元素同类型"]
A --> D["有序性<br/>按位置索引 1-based"]
A --> E["向量化<br/>运算自动逐元素"]
style A fill:#fff3cd
style B fill:#cce5ff
style C fill:#d4edda
style D fill:#f8d7da
style E fill:#e1d4ff
(2) R 向量 vs Python 列表
| 特性 | R 向量 c(1,2,3) |
Python 列表 [1,2,3] |
|---|---|---|
| 元素类型 | 必须相同 | 可以不同 |
| 长度可变性 | ❌ 不可变 | ✅ 可变(append/remove) |
| 算术运算 | 自动向量化 | 需要列表推导式 |
| 索引方式 | x[1](1-based) |
x[0](0-based) |
| 性能 | C 语言底层,快 | 通用对象,慢 |
# 混类型:数字和字符串混合 → 全部转字符串
c(1, "a", TRUE)
# [1] "1" "a" "TRUE"
(3) "标量" 在 R 里不存在
R 向量有个隐藏特性:单个数字也是长度为 1 的向量。
x <- 42
length(x)
# [1] 1
# 这就是为什么 R 的所有运算默认都是"向量化"的
c(1, 2, 3) + 10
# [1] 11 12 13
4. 4 种创建向量的方法
(1) 创建方式总览
| 方法 | 语法 | 适用场景 |
|---|---|---|
c() |
c(1, 2, 3) |
手动列几个数(最常用) |
: |
1:5 |
连续整数序列 |
seq() |
seq(1, 10, 2) |
等差数列 |
rep() |
rep(1:3, times = 2) |
重复元素 |
paste() |
paste("x", 1:3) |
字符串拼接 |
(2) 各种方法对比
# 1. c() —— concatenate(连接),最常用
c(10, 20, 30, 40, 50)
# [1] 10 20 30 40 50
# 2. : —— 冒号运算符,连续整数
1:5
# [1] 1 2 3 4 5
5:1
# [1] 5 4 3 2 1 ← 反向序列
# 3. seq() —— 等差数列
seq(from = 1, to = 10, by = 2)
# [1] 1 3 5 7 9
seq(from = 0, to = 1, length.out = 5)
# [1] 0.00 0.25 0.50 0.75 1.00
# 4. rep() —— 重复元素
rep(x = 1:3, times = 2)
# [1] 1 2 3 1 2 3 ← 整个向量重复 2 次
rep(x = 1:3, each = 2)
# [1] 1 1 2 2 3 3 ← 每个元素重复 2 次
# 5. paste() —— 字符串拼接
paste("第", 1:3, "名")
# [1] "第 1 名" "第 2 名" "第 3 名"
(3) 字符串向量与 paste()
# paste 默认 sep = " "(空格)
paste("Hello", "World")
# [1] "Hello World"
# paste0 = paste(..., sep = "") 无分隔符
paste0("Hello", "World")
# [1] "HelloWorld"
# 向量化拼接
paste(c("a", "b", "c"), 1:3, sep = "-")
# [1] "a-1" "b-2" "c-3"
paste() 与 paste0() 的区别是 paste0() 默认 sep = "",少打几个字,速度也快 30%。
5. 5 种访问向量元素的方式
(1) 访问方式总览
| 方式 | 语法 | 含义 |
|---|---|---|
| 正整数索引 | x[1] |
取第 1 个元素(R 是 1-based) |
| 负整数索引 | x[-1] |
排除第 1 个 |
| 多个索引 | x[c(1, 3)] |
取第 1 和第 3 个 |
| 逻辑索引 | x[x > 5] |
取大于 5 的元素 |
| 命名索引 | x["Bob"] |
按名字取 |
(2) ⚠️ 重要陷阱:R 是 1-based
x <- c("a", "b", "c", "d", "e")
x[1] # 第 1 个元素
# [1] "a"
x[0] # ← 错误!空向量,不是 "a"
# numeric(0)
x[c(1, 3, 5)] # 多个索引
# [1] "a" "c" "e"
x[0],得到的是空向量而不是第 1 个元素。
(3) 负整数索引(排除元素)
x <- c("a", "b", "c", "d", "e")
x[-1] # 排除第 1 个
# [1] "b" "c" "d" "e"
x[-c(1, 3)] # 排除第 1 和第 3 个
# [1] "b" "d" "e"
(4) 逻辑索引(最强大)
这是 R 里最强大的索引方式:
x <- c(10, 20, 30, 40, 50)
# 找出大于 30 的元素
x[x > 30]
# [1] 40 50
# 找出偶数
x[x %% 2 == 0]
# [1] 10 20 30 40 50
# 多个条件(& 表示"且",| 表示"或")
x[x > 20 & x < 50]
# [1] 30 40
x[逻辑向量],TRUE 的位置保留,FALSE 的位置过滤掉。
(5) 命名索引
给向量元素起名字,再用名字访问:
scores <- c(Alice = 85, Bob = 92, Charlie = 78)
scores
# Alice Bob Charlie
# 85 92 78
scores["Bob"]
# Bob
# 92
6. 向量运算:自动向量化
(1) 什么是"向量化"?
graph LR
A["x = 1, 2, 3"] --> C["+ 100"]
C --> D["101, 102, 103"]
E["x = 1, 2, 3"] --> F["+ y = 10, 20, 30"]
F --> G["11, 22, 33"]
style A fill:#cce5ff
style E fill:#cce5ff
style C fill:#fff3cd
style F fill:#d4edda
style D fill:#d4edda
style G fill:#d4edda
R 的算术运算默认都是"向量化"——不需要写循环。
(2) 向量 + 标量(标量"广播")
x <- c(1, 2, 3, 4, 5)
x + 100 # 100 广播到每个元素
# [1] 101 102 103 104 105
x * 10
# [1] 10 20 30 40 50
(3) 向量 + 向量(对应元素运算)
x <- c(1, 2, 3, 4, 5)
y <- c(10, 20, 30, 40, 50)
x + y
# [1] 11 22 33 44 55
x * y
# [1] 10 40 90 160 250
numpy 或列表推导式,R 一行搞定。
(4) 长度不同时会"循环"
c(1, 2, 3, 4) + c(10, 20)
# Warning: longer object length is not a multiple of shorter object length
# [1] 11 22 13 24
# 第二个向量循环:[10, 20, 10, 20]
(5) 常用数学函数(全部向量化)
| 函数 | 作用 | 示例 |
|---|---|---|
abs() |
绝对值 | abs(c(-1, -2, 3)) → 1 2 3 |
sqrt() |
平方根 | sqrt(c(1, 4, 9)) → 1 2 3 |
log() |
自然对数 | log(c(1, exp(1))) → 0 1 |
round() |
四舍五入 | round(3.14159, 2) → 3.14 |
floor() ceiling() |
向下/向上取整 | floor(1.6) → 1 |
7. 聚合函数:把向量塌缩成一个值
| 函数 | 作用 | 示例 |
|---|---|---|
sum() |
求和 | sum(1:5) → 15 |
mean() |
平均值 | mean(1:5) → 3 |
median() |
中位数 | median(1:5) → 3 |
min() max() |
最小最大 | max(1:5) → 5 |
sd() |
标准差 | sd(1:5) → 1.581 |
var() |
方差 | var(1:5) → 2.5 |
length() |
长度 | length(1:5) → 5 |
prod() |
连乘积 | prod(1:5) → 120 |
na.rm = TRUE 参数跳过 NA:mean(c(1, 2, NA), na.rm = TRUE) → 1.5
8. 常用操作:排序、去重、查找
(1) 三大操作速查表
| 类别 | 函数 | 作用 |
|---|---|---|
| 排序 | sort(x) |
排好序的新向量 |
| 排序 | order(x) |
排序后的索引位置 |
| 排序 | rev(x) |
反转向量 |
| 去重 | unique(x) |
唯一值 |
| 去重 | table(x) |
频次统计 |
| 集合 | union(a, b) |
并集 |
| 集合 | intersect(a, b) |
交集 |
| 集合 | setdiff(a, b) |
差集 |
| 查找 | match("a", x) |
第一次出现位置 |
| 查找 | "a" %in% x |
是否包含元素 |
(2) 关键:order() vs sort()
x <- c(3, 1, 4, 1, 5, 9, 2, 6)
# sort() 返回排好序的向量
sort(x)
# [1] 1 1 2 3 4 5 6 9
# order() 返回排序后的**索引**
order(x)
# [1] 2 4 7 1 3 8 6 5
# 即:x 中第 2 小的排第一,第 4 小排第二... 排序后 x[order(x)] = sort(x)
order() 在数据框排序时极其常用:df[order(df$age), ] 按年龄升序排列。
9. 完整示例:学生成绩统计
下面是一个完整工作流示例,把本课所有知识点串起来。
▶ 示例:30 个学生成绩综合分析
# ============================================
# 学生成绩综合分析
# 功能:创建成绩向量,做 5 种统计
# ============================================
# 1. 创建 5 个学生成绩(命名向量)
scores <- c(Alice = 85, Bob = 92, Charlie = 78, David = 95, Eve = 88)
cat("原始成绩:\n")
print(scores)
# 2. 基础统计
cat("\n=== 基础统计 ===\n")
cat("总分:", sum(scores), "\n")
cat("平均分:", mean(scores), "\n")
cat("中位数:", median(scores), "\n")
cat("最高分:", max(scores), "\n")
cat("最低分:", min(scores), "\n")
cat("标准差:", round(sd(scores), 2), "\n")
# 3. 找最高分学生(命名索引)
cat("\n=== 最高分学生 ===\n")
print(scores[scores == max(scores)])
# 4. 找出 90 分以上
cat("\n=== 90 分以上学生 ===\n")
print(scores[scores >= 90])
# 5. 找出不及格
cat("\n=== 不及格学生(< 60)===\n")
print(scores[scores < 60])
# 6. 排序(降序)
cat("\n=== 成绩降序 ===\n")
print(sort(scores, decreasing = TRUE))
# 7. 标准化(z-score:所有分数减去平均再除以标准差)
z_scores <- (scores - mean(scores)) / sd(scores)
cat("\n=== Z-Score 标准化 ===\n")
print(round(z_scores, 3))
# 8. 评级(优秀/良好/及格/不及格)
ratings <- ifelse(scores >= 90, "优秀",
ifelse(scores >= 80, "良好",
ifelse(scores >= 60, "及格", "不及格")))
cat("\n=== 评级 ===\n")
print(data.frame(分数 = scores, 评级 = ratings))
预期输出(节选):
原始成绩:
Alice Bob Charlie David Eve
85 92 78 95 88
=== 基础统计 ===
总分: 438
平均分: 87.6
中位数: 88
最高分: 95
最低分: 78
标准差: 6.19
=== 90 分以上学生 ===
David
95
=== 成绩降序 ===
David Bob Alice Eve Charlie
95 92 85 88 78
=== Z-Score 标准化 ===
Alice Bob Charlie David Eve
-0.420 0.711 -1.551 1.197 -0.097
(1) 操作步骤
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 新建 RStudio Project r-vectors |
File → New Project |
| 2 | 新建脚本 score_analysis.R |
复制上面代码 |
| 3 | 选中全部 → Ctrl+Enter |
发送到控制台 |
| 4 | 观察输出 | 看到 6 项统计结果 |
❓ 常见问题
x[0] 期望拿第 1 个元素,结果拿到空向量 numeric(0)。c(1,2,3,4,5,6) reshape 成 2×3 矩阵就是 matrix(1:6, nrow = 2))。📖 小节
- 向量是 R 最基本的数据结构:同类型元素的有序集合;R 里没有"标量",单个数字就是长度 1 的向量
- 创建方式:
c()直接列、:整数序列、seq()等差、rep()重复;字符串拼接用paste()/paste0() - 索引 4 种方式:正整数(1-based,
x[0]是空向量)、负整数(排除)、逻辑向量(筛选)、命名索引(by name) - 算术运算自动向量化(向量+标量、向量+向量),不用写循环;常用数学函数(sqrt/log/exp)也是逐元素
- 聚合函数(
sum()mean()sd()median())把向量塌缩成单个值;都支持na.rm = TRUE跳过 NA - 排序用
sort()/order()(order 返回索引),去重用unique(),频次用table(),集合用union/intersect/setdiff,查找用match()/%in%
📝 作业
-
基础题:用 3 种方法创建 1~10 的整数向量:①
c()②:③seq();用identical()验证三者结果完全相同。截图保存控制台输出。 -
基础题:创建向量
x <- c(2, 4, 6, 8, 10),用逻辑索引提取出大于 5 的元素,输出筛选后的向量。把代码和输出截图保存。 -
基础题:用
paste()拼接出paste("第", 1:5, "章", sep = "")的结果并记录;再用paste0()拼接paste0("x", 1:5, "y")的结果并记录。截图保存。 -
进阶题:模拟 10 个学生的成绩:① 用
sample(60:100, 10)随机生成;② 计算平均分、中位数、最高分、最低分、标准差;③ 用which.max()找出最高分学生位置;④ 统计及格(≥60)人数。把每一步输出截图保存。 -
挑战题:写一段脚本模拟"学生分班"场景:① 构造向量
names <- c("Alice", "Bob", ...)10 个学生名;② 构造scores <- sample(60:100, 10)10 个成绩;③ 用names(scores) <- names命名;④ 找出 90 分以上的学生姓名;⑤ 找出不及格学生姓名;⑥ 用sort(scores)升序排列打印。运行后截图保存控制台全部输出。