未记录事实核验 · 状态:现行内容
记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。
warning 适用边界与维护说明
本文适合学习基础 R 数据结构的创建、索引、运算与类型转换,示例和练习不依赖外部数据材料;“向量只能包含同一类型的元素”应限于原子向量理解,不涵盖列表这种异构容器。因子建模部分的哑变量说明应限于示例所用的无序因子及默认对比设置,不能直接推广到有序因子或自定义 contrasts。历史序列注释、整数类型示例及转换警告说明已修正,不再作为现存风险。本次仅作全文结构审阅,未运行代码或进行外部事实、关联链接核验。
R 数据结构
R 的核心优势在于它为统计计算设计了一套精妙的数据容器体系。理解这套体系,是写出地道 R 代码的前提。本文系统梳理六种核心数据结构的创建、索引、运算和选型逻辑。
目录
向量 Vector
向量是 R 的基本单元。所有标量在 R 中实际上都是长度为 1 的向量。本节原子向量只能包含同一类型的元素,这一限制使得向量运算可以被高度向量化。
创建方式
# c() 是最通用的构造函数
scores <- c(85, 92, 78, 96, 88)
cities <- c("北京", "上海", "广州")
flags <- c(TRUE, FALSE, TRUE)
# seq() 生成等差序列
seq(1, 10) # 1 2 3 4 5 6 7 8 9 10
seq(0, 1, by = 0.25) # 0.00 0.25 0.50 0.75 1.00
seq(1, 10, length.out = 4) # 生成4个等间距的值: 1.000000 4.000000 7.000000 10.000000
# 简写运算符 ":"
1:5 # 1 2 3 4 5
5:1 # 5 4 3 2 1(可倒序)
# rep() 生成重复模式
rep(0, times = 5) # 0 0 0 0 0
rep(c(1, 2), times = 3) # 1 2 1 2 1 2
rep(c(1, 2), each = 3) # 1 1 1 2 2 2(each 与 times 的区别)
rep(c("A","B","C"), times = c(2, 1, 3)) # A A B C C C
类型强制转换规则
当 c() 中混入不同类型时,R 会按以下优先级向上转换(coercion):
logical < integer < double < complex < character
c(TRUE, 1L) # logical → integer: 1 1
c(TRUE, 1L, 1.5) # integer → double: 1.0 1.0 1.5
c(1, "a") # double → character: "1" "a"
c(TRUE, "x") # logical → character: "TRUE" "x"
warning 混合类型向量是 R 新手常见的 bug 来源。如果一列数据意外被转成了 `character`,直接参与算术通常会报错;将无法解析的字符串转换为数值时,则可能产生 NA 和警告。可先用 `class()` 检查该列类型。
向量索引
R 的索引从 1 开始,支持四种方式:
x <- c(10, 20, 30, 40, 50)
names(x) <- c("a", "b", "c", "d", "e")
# 1. 位置索引(正整数)
x[1] # 10
x[c(1, 3, 5)] # 10 30 50
x[1:3] # 10 20 30
# 2. 负索引(排除)
x[-1] # 20 30 40 50(去掉第1个)
x[-c(2, 4)] # 10 30 50(去掉第2和第4个)
# 3. 逻辑索引
x[x > 25] # 30 40 50
x[c(TRUE, FALSE, TRUE, FALSE, TRUE)] # 10 30 50
# 4. 名称索引
x["a"] # a: 10
x[c("a","c")] # a:10 c:30
向量化运算(element-wise)
R 的运算符默认逐元素操作,无需循环:
a <- c(1, 2, 3, 4)
b <- c(10, 20, 30, 40)
a + b # 11 22 33 44
a * b # 10 40 90 160
a ^ 2 # 1 4 9 16
sqrt(a) # 1.000 1.414 1.732 2.000
# 循环补齐(recycling):短向量会被循环复用
a + c(100, 200) # 101 202 103 204(100,200 循环了一次)
note 循环补齐只有在长度成整数倍时才不会报警告。长度不整除时 R 会警告,但仍会执行,容易造成隐蔽错误。
矩阵 Matrix
矩阵是二维同类型数据结构,是 dim 属性为 c(nrow, ncol) 的向量。
创建方式
# matrix() 默认按列填充(byrow = FALSE)
m1 <- matrix(1:12, nrow = 3, ncol = 4)
# [,1] [,2] [,3] [,4]
# [1,] 1 4 7 10
# [2,] 2 5 8 11
# [3,] 3 6 9 12
# byrow = TRUE 改为按行填充
m2 <- matrix(1:12, nrow = 3, byrow = TRUE)
# [,1] [,2] [,3] [,4]
# [1,] 1 2 3 4
# [2,] 5 6 7 8
# [3,] 9 10 11 12
# 通过 dim 属性把向量变成矩阵
v <- 1:6
dim(v) <- c(2, 3) # 现在 v 是一个 2×3 矩阵
# cbind/rbind 合并向量
m3 <- cbind(c(1,2,3), c(4,5,6)) # 按列合并
m4 <- rbind(c(1,2,3), c(4,5,6)) # 按行合并
矩阵运算
A <- matrix(c(1,2,3,4), 2, 2)
B <- matrix(c(5,6,7,8), 2, 2)
A * B # 逐元素乘法(element-wise)
A %*% B # 真正的矩阵乘法(线性代数)
t(A) # 转置
solve(A) # 求逆矩阵(A 必须可逆)
det(A) # 行列式
tip `*` 是逐元素乘,`%*%` 是矩阵乘。这是 R 与数学符号不一致的地方,新手极易混淆。在做 PCA、回归系数计算等场景,务必使用 `%*%`。
apply 函数族
m <- matrix(1:12, nrow = 3)
apply(m, 1, sum) # MARGIN=1: 对每一行求和,返回长度3的向量
apply(m, 2, mean) # MARGIN=2: 对每一列求均值,返回长度4的向量
apply(m, 1, function(row) row / sum(row)) # 行归一化
# 常用快捷函数(效率高于 apply)
rowSums(m); colSums(m)
rowMeans(m); colMeans(m)
数组 Array
数组是矩阵的推广,支持三维及以上。dim 参数按 c(行, 列, 层, ...) 排列。
# 创建一个 2×3×4 的三维数组
arr <- array(1:24, dim = c(2, 3, 4))
dim(arr) # 2 3 4
length(arr) # 24
# 切片访问
arr[1, , ] # 第1行的所有元素(2维结果)
arr[, 2, ] # 第2列的所有元素
arr[, , 3] # 第3层(返回一个 2×3 矩阵)
arr[1, 2, 3] # 精确定位单个元素
# apply 可作用于任意维度
apply(arr, 3, sum) # 对第3维(层)求和,返回长度4的向量
note 三维数组在时间序列(样本 × 特征 × 时间步)和图像处理(高 × 宽 × 通道)中常见。日常数据分析中使用频率远低于前几种结构。
列表 List
列表是 R 的异构容器,每个元素可以是任意类型、任意长度,甚至可以嵌套列表。
创建方式
student <- list(
name = "张三",
age = 22,
scores = c(88, 92, 95),
info = data.frame(course = c("数学","英语"), credit = c(4, 3))
)
[[]] 与 [] 的关键区别
这是列表操作中最容易出错的地方:
| 语法 | 返回类型 | 说明 |
|---|---|---|
x[[1]] |
元素本身(任意类型) | 取出元素,"开箱" |
x[1] |
仍是列表 | 保留外层列表,返回子列表 |
x$name |
元素本身 | 等同于 x[["name"]],按名称取 |
student[[1]] # "张三"(字符型)
student[1] # 列表,包含 $name = "张三"
student$name # "张三"(同 [[]])
student[["scores"]] # 88 92 95(数值向量)
# 嵌套访问
student$info$course # "数学" "英语"
student[["info"]][["credit"]] # 4 3
lapply / sapply 批量操作
numbers <- list(a = 1:5, b = 6:10, c = 11:15)
lapply(numbers, mean) # 返回列表:list(a=3, b=8, c=13)
sapply(numbers, mean) # 返回命名向量:a=3 b=8 c=13(自动简化)
sapply(numbers, range) # 返回矩阵(每列一个元素的结果)
# vapply 是 sapply 的类型安全版本(推荐生产代码使用)
vapply(numbers, mean, numeric(1)) # 明确指定返回类型
tip `sapply` 在结果类型不确定时可能返回列表或矩阵,行为不稳定。在需要稳定性的生产代码中,优先用 `lapply` 或 `vapply`。
数据框 Data Frame
数据框是 R 中最重要的数据结构,本质上是等长向量组成的列表。每列可以是不同类型,每行代表一个观测。
创建方式
df <- data.frame(
name = c("张三", "李四", "王五"),
age = c(28, 32, 25),
score = c(88.5, 91.0, 76.5),
passed = c(TRUE, TRUE, FALSE),
stringsAsFactors = FALSE # R 3.x 默认 TRUE,R 4.x 默认 FALSE
)
warning `stringsAsFactors = TRUE` 是 R 3.x 数据框的经典陷阱:字符串列被自动转成因子,导致字符串操作报错。R 4.0 已将默认值改为 `FALSE`,但仍要注意遗留代码。
行列访问方式
# $ 符号(最常用,按列名)
df$name # 返回字符向量
df$age[2] # 第2个人的年龄
# [行, 列] 下标(灵活)
df[1, ] # 第1行(返回单行数据框)
df[, "score"] # score 列(返回向量)
df[2, "age"] # 第2行的 age 值:32
# [[ ]] 按列名(返回向量,不是数据框)
df[["name"]] # 等同于 df$name
# subset() 更具可读性的条件筛选
subset(df, age > 27) # 年龄大于27
subset(df, passed, select = name) # 只返回 passed=TRUE 的姓名列
添加与修改列
df$grade <- ifelse(df$score >= 90, "A", "B") # 新增衍生列
df[["rank"]] <- rank(-df$score) # 按分数排名
# 批量修改(向量化)
df$score <- df$score * 1.05 # 所有分数提高5%
因子 Factor
因子用于存储有限类别的数据,底层是整数+水平标签映射。它在统计建模中有特殊意义:线性模型按因子的contrasts设置编码;无序因子的默认设置通常采用处理对比,有序因子的默认设置不同,不能统一视为哑变量编码。
创建与水平控制
size <- factor(c("S", "M", "L", "M", "XL", "S"))
levels(size) # "L" "M" "S" "XL"(默认字典序)
nlevels(size) # 4
# 手动指定水平顺序(有序因子)
size_ordered <- factor(
c("S", "M", "L", "M", "XL"),
levels = c("S", "M", "L", "XL"), # 明确顺序
ordered = TRUE # 建立大小关系
)
size_ordered[1] < size_ordered[3] # TRUE(S < L)
# 修改水平标签
levels(size) <- c("大", "中", "小", "加大")
统计与建模应用
region <- factor(c("北京","上海","北京","广州","上海","北京"))
table(region) # 各水平频次统计
prop.table(table(region)) # 各水平比例
# 本例无序因子且采用默认处理对比时,在lm()中展开为相应虚拟变量
df_model <- data.frame(
y = c(10,12,8,15,11),
group = factor(c("A","B","A","B","C"))
)
model <- lm(y ~ group, data = df_model)
# R 自动以 "A" 为基准,生成 groupB 和 groupC 两个虚拟变量
tip 当你读入分类数据后要做回归,先检查该列是 `character` 还是 `factor`。`character` 在 `lm()` 中会被自动转换,但在某些包(如 `glmnet`)中不会,建议显式转换。
数据结构选型对比
| 结构 | 创建函数 | 维度 | 元素类型 | 典型用途 |
|---|---|---|---|---|
| 向量 Vector | c(), seq(), rep() |
1维 | 单一类型 | 数值序列、字符列表 |
| 矩阵 Matrix | matrix(), cbind(), rbind() |
2维 | 单一类型 | 数值计算、相关系数矩阵 |
| 数组 Array | array() |
N维 | 单一类型 | 三维时序数据、图像张量 |
| 列表 List | list() |
任意嵌套 | 任意类型 | 模型结果、配置参数 |
| 数据框 Data Frame | data.frame() |
2维(列列表) | 各列可不同 | 表格数据、业务数据集 |
| 因子 Factor | factor() |
1维 | 整数+标签 | 分类变量、有序变量 |
# 判断数据结构
x <- data.frame(a = 1:3)
is.data.frame(x) # TRUE
is.list(x) # TRUE(数据框是特殊列表)
class(x) # "data.frame"
typeof(x) # "list"
# 查看内部结构
str(x)
类型检查与转换速查
is.* 检查函数
| 函数 | 返回 TRUE 的情况 |
|---|---|
is.numeric(x) |
数值型(double 或 integer) |
is.integer(x) |
整数型(如 1L、1:3 或 as.integer() 的结果) |
is.double(x) |
双精度浮点型 |
is.character(x) |
字符型 |
is.logical(x) |
逻辑型 |
is.factor(x) |
因子型 |
is.na(x) |
值为 NA(逐元素) |
is.null(x) |
空对象 |
is.vector(x) |
无属性或仅有名称属性的向量(慎用) |
is.list(x) |
列表(含数据框) |
as.* 转换函数
as.numeric("3.14") # 3.14
as.integer(3.9) # 3(截断,不四舍五入)
as.character(123) # "123"
as.logical(0) # FALSE(0→FALSE,非0→TRUE)
as.Date("2024-03-15") # Date 对象
# 以下无效字符串转换为数值时产生 NA,并伴随警告
as.numeric("abc") # NA,伴随警告 "NAs introduced by coercion"
as.integer("3.5b") # NA
# 安全检查的常用模式
x_raw <- c("1", "2", "abc", "4")
x_num <- suppressWarnings(as.numeric(x_raw))
sum(is.na(x_num)) # 统计转换失败的个数:1
warning `as.numeric()` 等转换遇到无效值时可能产生 NA 和警告;不同的 `as.*` 函数也可能报错。生产代码应检查转换结果中的 NA 是否符合预期,否则可能影响后续计算。
掌握检查
完成本文后,你应当能够回答以下问题:
- [ ]
c(TRUE, 1L, 2.5, "x")的结果是什么类型?各元素是什么值? - [ ]
rep(c(1,2), each=2)与rep(c(1,2), times=2)的输出有何不同? - [ ] 矩阵的
*和%*%各适用于什么计算场景? - [ ] 列表的
x[[1]]和x[1]返回的类型有何区别? - [ ] 在处理 R 3.x 遗留代码时,为什么显式指定
stringsAsFactors = FALSE有帮助?
练习题
题一(中等):创建一个包含10名学生的数据框,字段包括姓名、数学成绩(随机整数 60-100)、语文成绩(随机整数 60-100)。要求:
- 新增"总分"列;
- 用负索引去掉总分最低的学生那一行;
- 将数学成绩按"优秀(≥90)/ 良好(80-89)/ 及格(60-79)"转换为有序因子,并用
table()统计各等级人数。
题二(提高):用 lapply 对一个包含5个数值向量的列表,同时计算每个向量的均值、标准差和四分位距(IQR),最后用 do.call(rbind, ...) 将结果合并成一个数据框。
关联文档:数据导入导出 | dplyr数据操作 | 数据清洗技巧
创建时间:2024-12-11 | 更新时间:2026-10-09 预计学习时长:60-75分钟
#R语言 #数据结构 #向量 #矩阵 #数据框 #列表 #因子 #类型转换