最简R语言教程(一)

基础语法篇-数据结构

R语言
基础语法
Author

Lee

Published

April 29, 2026

1 向量

向量一维同类型数据的容器,例如全部是数值、全部是字符,或者全部是逻辑值。

这种统一的数据类型让向量在计算时更高效,也更容易进行批量操作。比如,对一个数值向量可以直接做加减乘除,对一个字符向量可以进行拼接和匹配。由于向量在 R 里非常常用,很多更复杂的数据对象,实际上都可以看作是建立在向量基础上的。

在实际编程中,向量常用于存储一组观测值、特征值或标签。例如,温度记录、学生成绩、基因表达值等,都可以用向量来表示。

理解向量的概念,是进一步学习 R 数据处理和统计分析的基础。

1.1 创建向量

# 创建简单数值向量
1:5
[1] 1 2 3 4 5
x <- c(10, 20, 30, 40, 50)

# 创建特殊形式的向量
seq(1, 10, by = 2) # 等差数列
[1] 1 3 5 7 9
rep(c("A", "B"), times = 3) # 重复元素向量
[1] "A" "B" "A" "B" "A" "B"
rep(1:3, each = 2) # 每个元素重复指定次数
[1] 1 1 2 2 3 3
rnorm(5) # 生成5个标准正态分布的随机数
[1]  1.0550881 -1.0087725 -1.0184400 -0.7927529 -0.1083850
sample(1:100, size = 10) # 从1到100中随机抽取10个数
 [1] 97 61 46 26 17 94 21 28 52 33
Note

norm() 系列函数是 R 中围绕正态分布的一组常用函数,主要用于生成、计算和查询正态分布相关信息。它们的命名通常遵循统一规则:d 表示密度函数,p 表示分布函数,q 表示分位数函数,r 表示随机生成函数。因此,dnorm()pnorm()qnorm()rnorm() 分别对应正态分布的不同操作。

其中,dnorm() 用于计算某个点的概率密度,pnorm() 用于计算累积概率,qnorm() 用于根据概率反推对应的分位数,rnorm() 用于生成服从正态分布的随机数。

这一组函数在统计分析、概率计算、模拟实验和数据建模中非常重要,是处理正态分布问题的基础工具。

1.2 向量索引

R中向量的索引是从1开始的,可以使用方括号[]来访问向量中的元素。如果对向量各个元素进行了命名,那么也可以通过名称来访问元素,例如x["name"]

x[1] # 访问第一个元素
[1] 10
x[2:4] # 访问第二到第四个元素
[1] 20 30 40
x <- setNames(x, c("A", "B", "C", "D", "E")) # 给向量元素命名
x["C"] # 通过名称访问元素
 C 
30 
x[["C"]] # 通过名称访问元素,返回元素本身
[1] 30
Important区分[][[]]

R 里,[][[]] 都用于取子集,但含义不同。z

  • [] 是“取子集”,返回的通常还是原来的容器类型。比如对向量、列表、数据框使用 [],得到的仍然是一个向量、列表或数据框的子集,而不是单个元素本身。

  • [[]] 是“取单个元素”,直接把元素拿出来。它常用于列表和数据框列,返回的是元素本身,而不是外层容器。

简单理解:

  • []:保留“盒子”,例如x[1]取子集,返回一个长度为1的向量,仍然是一个“盒子”。

  • [[]]:拆开“盒子”,拿出里面那一个对象,例如x[[1]]直接返回第一个元素的值,不再是一个向量。

1.3 逻辑向量

逻辑向量是由 TRUEFALSE 组成的向量,常用于条件判断和筛选数据。它们在 R 中非常重要,因为很多函数和操作都依赖于逻辑向量来实现过滤、子集选择和条件计算。

  • 通过比较运算符(如 >, <, ==, != 等)生成逻辑向量。例如,x > 30 会返回一个与 x 长度相同的逻辑向量,表示每个元素是否大于30。
  • 通过逻辑运算符(如 &, |, !)进行组合。例如,(x > 20) & (x < 40) 会返回一个逻辑向量,表示哪些元素同时满足大于20且小于40的条件。
  • 通过%in% 运算符检查该符号左边的元素是否属于符号右侧的向量中。例如,x %in% c(20, 30) 会返回一个逻辑向量,表示哪些元素是20或30。
  • == 运算符用于比较元素是否相等,返回一个逻辑向量。例如,x == 30 会返回一个逻辑向量,表示哪些元素等于30。

1.4 向量有关的函数

  • names(x) = NULL:去掉向量的名称属性。
  • length(x):返回向量的长度。
  • sort(x):对向量进行排序,默认升序。decreasing = TRUE 参数可以指定降序。
  • order(x):返回向量元素排序后的索引位置。

1.5 向量化思维

向量化思维是 R 里非常重要的一种编程方式。它的核心思想是:尽量把对单个元素的操作,改写成对整个向量的批量操作。这样不仅代码更简洁,也通常更高效,因为 R 本身对向量运算做了很多优化。

请记住:一次至少操作一个向量,即同时对若干元素做相同的计算 —— 不要逐元素处理,要整体思考。

例如,与其用循环逐个处理每个数值,不如直接对整个向量做加减乘除、比较、筛选或转换。很多常见任务都可以直接用向量化写法完成,比如计算一组成绩的平方、判断哪些值大于某个阈值,或者对一列数据统一做标准化。

理解向量化思维,有助于写出更符合 R 风格的代码。初学者常常先想到 for 循环,但在 R 中,优先考虑向量运算往往是更自然的选择。它能减少重复代码,也让程序更容易读懂和维护。

2 矩阵

矩阵二维同类型数据的容器,可以看作是向量的扩展。矩阵中的元素必须是同一类型(数值、字符或逻辑),但它们被组织成行和列的结构。

2.1 创建矩阵

set.seed(1)
x <- sample(1:100, size = 12) # 生成12个随机数
m <- matrix(x, nrow = 3, ncol = 4) # 创建3行4列的矩阵
m
     [,1] [,2] [,3] [,4]
[1,]   68   34   14   51
[2,]   39   87   82   85
[3,]    1   43   59   21

2.2 矩阵索引

m[2, ] # 访问第二行所有列
[1] 39 87 82 85
m[, 3] # 访问第三列所有行
[1] 14 82 59
m[2:3, ] # 访问第二行和第三行所有列
     [,1] [,2] [,3] [,4]
[1,]   39   87   82   85
[2,]    1   43   59   21
m[2:3, c(1, 4)] # 访问第二行和第三行的第一列和第四列
     [,1] [,2]
[1,]   39   85
[2,]    1   21
m[m > 70] # 访问矩阵中大于70的元素
[1] 87 82 85

2.3 矩阵运算

矩阵支持各种数学运算,例如加法、乘法、转置等。矩阵运算通常遵循线性代数的规则,可以用于数据分析、统计建模和机器学习等领域。

  • +, -, *, /: 两个同型矩阵, 对应元素分别做四则运算。
  • %*%: 矩阵乘法。
w <- c(0.2, 0.3, 0.1, 0.4) # 权重向量 
m %*% w # 矩阵乘法,得到一个新的向量
     [,1]
[1,] 45.6
[2,] 76.1
[3,] 27.4

2.4 矩阵有关的函数

m1 <- m[, -4] # 去掉第四列,得到一个新的矩阵
diag(m1) # 提取矩阵的对角线元素
[1] 68 87 59
solve(m1) # 计算拟矩阵
            [,1]        [,2]        [,3]
[1,]  0.02865038 -0.02503120  0.02799073
[2,] -0.03956142  0.07127830 -0.08967730
[3,]  0.02834730 -0.05152434  0.08183277
eigen(m1) # 计算矩阵的特征值和特征向量
eigen() decomposition
$values
[1] 147.428404  60.257792   6.313803

$vectors
           [,1]        [,2]       [,3]
[1,] -0.4193271 -0.90673975  0.2747165
[2,] -0.8145532  0.03338313 -0.7474673
[3,] -0.4008340  0.42036722  0.6048335

3 列表

列表是R中一种灵活的数据结构,可以包含不同类型的元素,包括向量、矩阵、数据框等。列表中的每个元素都可以有自己独特的属性和结构。

列表中的每个元素都可以拥有自己独立的长度、类型和属性,因此它不像矩阵那样要求所有元素规则排列。正因为如此,列表常被用来保存模型结果统计分析输出多部分数据集。通过列表,用户可以更方便地把相关内容组合在一起,并按名称或位置对元素进行访问和管理。

3.1 创建列表

l1 <- list(a = 1:3, b = c(TRUE, FALSE), c = "abcd")
l1
$a
[1] 1 2 3

$b
[1]  TRUE FALSE

$c
[1] "abcd"

3.2 访问列表

  • Section 1.2 中介绍的向量索引类似,列表也可以通过位置或名称来访问元素。使用单方括号 [] 返回的是一个子列表,而使用双方括号 [[]] 则直接返回元素本身。
  • unlist() 函数可以将列表平摊开,转换成一个向量。
  • str() 函数可以查看列表的结构,帮助理解列表中各个元素的类型和内容。
l1[2] # 返回一个包含第二个元素的子列表
$b
[1]  TRUE FALSE
l1[[2]] # 直接返回第二个元素,即逻辑向量
[1]  TRUE FALSE

4 数据框

数据框,是由列向量组成、有着矩阵形式的列表:行对应观测(记录),列对应变量(特征)。与纯矩阵不同,数据框允许不同列有不同的数据类型(数值、字符、因子/分类、日期等),并通常带有列名和可选的行名。

R中,更建议使用现代的数据框格式tibble,它是 data.frame 的增强版本,提供了更友好的打印输出、更严格的类型检查和更方便的数据操作功能。

我们依旧可以使用[][[]]$等方式访问数据框中的列或元素。
# 按列创建
df1 <- tibble(
  ID = 1:3,
  name = c("Alice", "Bob", "Charlie"),
  height = c(165, 180, 175),
  weight = c(55, 75, 68)
)
df1
# A tibble: 3 × 4
     ID name    height weight
  <int> <chr>    <dbl>  <dbl>
1     1 Alice      165     55
2     2 Bob        180     75
3     3 Charlie    175     68
# 按行创建
df2 <- tribble(
  ~ID, ~name, ~height, ~weight,
  1, "Alice", 165, 55,
  2, "Bob", 180, 75,
  3, "Charlie", 175, 68
)
df2
# A tibble: 3 × 4
     ID name    height weight
  <dbl> <chr>    <dbl>  <dbl>
1     1 Alice      165     55
2     2 Bob        180     75
3     3 Charlie    175     68

5 因子

因子R中用于处理分类数据的一种特殊数据类型。它本质上是一个整数向量,每个整数对应一个类别(水平),并且有一个关联的字符向量存储这些类别的名称。因子在统计建模和数据分析中非常重要,因为它们可以帮助我们正确地处理分类变量,并且在某些模型中,因子会被自动识别为分类特征。

  • 通过factor()函数创建因子,可以指定类别的水平和顺序。levels 参数可以用来定义因子的水平,ordered 参数可以指定是否为有序因子,labels 参数可以用来为水平指定标签。
  • 使用cut(x, breaks, labels, ...)函数可以将连续变量转换为因子,常用于分组和分类。
# 创建一个有序因子,指定水平和标签
x <- c("中", "及格", "优", "优", "良")
f <- factor(
  x,
  levels = c("及格", "中", "良", "优"),
  ordered = TRUE,
  c("Pass", "Fair", "Good", "Excellent")
) # 创建有序因子, 并指定水平和标签
f
[1] Fair      Pass      Excellent Excellent Good     
Levels: Pass < Fair < Good < Excellent
# 使用 cut() 将连续变量分组为因子
scores <- c(65, 55, 75, 85, 95)
score_factor <- cut(
  scores,
  breaks = c(0, 60, 70, 80, 100),
  labels = c("不及格", "及格", "良", "优")
)
score_factor
[1] 及格   不及格 良     优     优    
Levels: 不及格 及格 良 优

forcats 包提供了一系列函数来创建和操作因子,使得处理分类数据更加方便和高效。通过使用 forcats,我们可以轻松地重新编码因子水平、调整水平的顺序、合并水平等,从而更好地适应数据分析的需求。

  • fct_recode():修改因子水平。它适合做术语统一(比如把 "M"/"F" 改成 "Male"/"Female")。常见坑是:旧水平名写错时不会按你预期重命名,结果可能保留原值或变成缺失,建议先检查 levels()
  • fct_collapse():折叠因子水平。比如把 "Excellent""Good" 合并为 "High"。这一步常用于减少类别稀疏、提高汇总可读性。与 fct_recode() 的区别是:recode 是“改名字”,collapse 是“并组”。
  • fct_lump_*():合并少数水平到“其它”。典型作用是把低频水平归到 "Other",避免长尾类别把图表和模型搞得很碎。你可以按频数阈值、前 n 个、比例等规则保留主要类别。
  • fct_reorder():重排序水平(常用于可视化),特别适合柱状图/箱线图,让类别按均值、中位数或其他统计量排序,而不是按字母顺序。这样可视化会更符合读者认知。

6 字符串

stringr包提供了一系列接口一致的、简单易用的字符串操作函数,足以代替R自带字符串函数。

x <- c("banana", "apple", "pear")

# 合并字符串
str_c(x, 1:3, sep = "_") # 将字符串与数字结合,使用下划线分隔
[1] "banana_1" "apple_2"  "pear_3"  
str_c(x, 1:3, collapse = "_") # 将字符串与数字结合,并将结果合并成一个字符串,使用下划线分隔
[1] "banana1_apple2_pear3"
str_c(x, collapse = ", ") # 将字符串向量合并成一个字符串,使用逗号和空格分隔
[1] "banana, apple, pear"
# 格式化输出-把变量嵌入固定的字符串模版中
str_glue("Pi = {pi}") # 使用 str_glue 格式化字符串,插入变量值
Pi = 3.14159265358979
df <- mtcars[1:2,]
str_glue_data(df, "The {rownames(df)} 总功率为 {hp} kW.") # 使用 str_glue_data 格式化字符串,插入数据框中的变量值
The Mazda RX4 总功率为 110 kW.
The Mazda RX4 Wag 总功率为 110 kW.
# 查找匹配-字符串模式匹配和提取
str_detect(x, "e") # 检测字符串中是否包含字母 "e"
[1] FALSE  TRUE  TRUE
str_extract(x, "e") # 提取字符串中第一个匹配的字母 "e"
[1] NA  "e" "e"
# 字符串拆分
x <- "10, 8, 7"
str_split(x, ", ") # 将字符串按逗号和空格分隔成一个列表
[[1]]
[1] "10" "8"  "7" 
str_split_1(x, ", ") # 将字符串按逗号和空格分隔成一个向量
[1] "10" "8"  "7" 

7 时间日期

lubridate 包提供了一系列更方便的函数,生成、转换、管理日期时间数据,足以代替 R 自带的日期时间函数。

注意:不要直接以字符串的形式处理日期时间数据,要先转化为日期时间对象。
today() # 获取当前日期
[1] "2026-04-29"
now() # 获取当前日期和时间
[1] "2026-04-29 15:43:38 CST"
# 创建日期对象
x <- c("2024-01-01", "2025-08-15", "2026-09-30")
x1 <- ymd(x) # 将字符串转换为日期对象,ymd 表示年月日的顺序
x1
[1] "2024-01-01" "2025-08-15" "2026-09-30"
t1 <- ymd_hms("2024-01-01 12:00:00") # 创建一个日期时间对象,ymd_hms 表示年月日时分秒的顺序

# 提取日期时间组件
year(x1) # 提取年份,类似的还有 month()、day()、week() 等函数
[1] 2024 2025 2026
quarter(x1) # 提取季度
[1] 1 3 3
wday(x1, label = TRUE) # 提取星期几,label = TRUE 返回星期名称
[1] 周一 周五 周三
Levels: 周日 < 周一 < 周二 < 周三 < 周四 < 周五 < 周六
hour(t1) # 提取小时
[1] 12
# 日期时间运算
x1 + 10 # 日期加上10天,即得到10天后的日期
[1] "2024-01-11" "2025-08-25" "2026-10-10"
x1 %m+% months(2) # 日期加上2个月,使用 %m+% 可以正确处理月末日期
[1] "2024-03-01" "2025-10-15" "2026-11-30"
rollbackward(x1, roll_to_first = TRUE) # 回滚到当月第一天
[1] "2024-01-01" "2025-08-01" "2026-09-01"
round_date(t1, "hour") # 将日期时间四舍五入到最近的小时。
[1] "2024-01-01 12:00:00 UTC"
floor_date(t1, "hour") # 将日期时间向下取整到最近的小时。
[1] "2024-01-01 12:00:00 UTC"
ceiling_date(t1, "hour") # 将日期时间向上取整到最近的小时。
[1] "2024-01-01 12:00:00 UTC"
# 计算年龄
birth <- ymd("1998-11-10") # 生日日期
gap <- birth %--% today() # 计算从生日到今天的时间区间
gap
[1] 1998-11-10 UTC--2026-04-29 UTC
as.period(gap) # 将时间区间转换为周期对象
[1] "27y 5m 19d 0H 0M 0S"
gap %/% years(1) # 计算年龄,得到一个数值,表示年龄的年数
[1] 27
x1 %within% gap # 判断日期 x1 是否在时间区间 gap 内,返回一个逻辑值
[1]  TRUE  TRUE FALSE