跳到主要内容

R 语言基础

本文介绍检查和转换小型内存数据集所需的基础 R 操作,假设已经安装 R。在控制台逐条运行表达式可以查看结果;把表达式保存成脚本,便于重复执行。统计推断和面向对象系统不在本篇范围内。语言行为以官方手册 An Introduction to R 为参考。

可以在 webR 浏览器终端中试运行这里的 base R 向量例子:先对比 1:0 和 seq_along(numeric(0)),再测试向量循环补齐。用这个小型练习环境观察语言行为,文件和项目依赖的练习再放回自己的 R 项目中。

基本语法​

程序构成要素​

  • 数据结构:数据的容器(向量、矩阵、数据框、列表等)
  • 算法:处理数据的逻辑步骤

表达式与赋值​

x <- c(2, 4, 6)
x + 1 # 3 5 7
sum(x) # 12
identical(1, 1L) # FALSE

<- 把名称绑定到值,== 比较值。R 区分大小写,# 开始注释。1 通常是 double,1L 是 integer;class() 描述对象参与方法调用时的类别,typeof() 描述内部类型。组合原子值时会转为共同类型:c(1, "2") 是字符向量,不是数值与文本的混合向量。

数据结构​

基本数据类型​

  1. 数值型 (Numeric):包含整数和浮点数
  2. 字符型 (Character):文本数据
  3. 逻辑型 (Logical):布尔值(TRUE, FALSE)

向量 (Vectors)​

原子向量的元素类型相同,普通标量是长度为 1 的向量。R 的术语中列表也属于向量,但可以容纳不同类型。

创建数值向量​

c(1.70, 1.72, 1.80, 1.66, 1.65, 1.88)

检查数据类型​

typeof(3.14)
class(3.14)

向量运算​

heights <- c(1.70, 1.72, 1.80, 1.66, 1.65, 1.88)
mean(heights)
sd(heights)

索引、循环补齐与缺失值​

取子集使用从 1 开始的下标。正下标选择元素,负下标排除元素,零不选择任何元素;除了零,不要混用正负下标。

x <- c(10, 20, NA_real_, 40)
x[c(1, 4)] # 10 40
x[-2] # 10 NA 40
x[!is.na(x) & x > 15] # 20 40
mean(x) # NA
mean(x, na.rm = TRUE) # 23.33333...
stopifnot(isTRUE(all.equal(mean(x, na.rm = TRUE), 70 / 3)))
c(1, 2, 3, 4) + c(10, 20) # 11 22 13 24

NA 表示缺失,不是零;用 is.na() 检查,而不是 x == NA。NULL 表示不存在,长度为零;NaN 是未定义的数值结果,Inf 是无穷大。is.na() 也识别 NaN;is.finite() 排除缺失和无穷值。忽略缺失值会改变汇总所用的样本,是否适合这样做要由分析问题决定。

向量算术会循环使用较短向量。长度不是整倍数时会警告,是整倍数时即使不符合本意也可能静默补齐。对于一一对应的测量值,应检查长度相等,除非确实要把一个标量应用到所有元素。

字符向量​

c("Male", "Female", "Female", "Male")

因子 (Factors)​

因子用于存储分类数据,并明确其水平(Levels)。

sex <- factor(c("Male", "Female", "Female", "Male"))
levels(sex)

逻辑向量​

支持向量化比较和逻辑索引。

heights > 1.7
heights[heights > 1.7]

数组与矩阵​

矩阵是二维数组,数组支持更高维度。

matrix(1:12, nrow = 4, ncol = 3)

数据框 (Data Frames)​

数据框是表格结构,每列可以是不同类型(如字符、数值、因子)。

df <- data.frame(
sex = c("F", "M", "M", "F"),
age = c(17, 29, 20, 33),
heights = c(1.66, 1.84, 1.83, 1.56)
)
str(df)

列表 (Lists)​

列表是通用容器,可混合存储不同长度和类型的对象。

l <- list(
sex = c("F", "M"),
age = c(17, 29, 20),
heights = c(1.66, 1.84, 1.83, 1.56)
)
l$sex

取出容器还是其中的内容​

is.list(l["sex"]) # TRUE: a one-element list
is.character(l[["sex"]]) # TRUE: its character vector
is.data.frame(df[, "age", drop = FALSE]) # TRUE
is.numeric(df[["age"]]) # TRUE

[ 选取子集,[[ 取出一个元素;对数据框而言,这个元素通常是一列。$sex 按名称取元素。数据框各列的行数必须相同,普通列表各元素则没有这个要求。选择行列后仍需保留二维表格时,使用 drop = FALSE。矩阵默认按列填充,所以 matrix(1:12, nrow = 4) 的第一列是 1、2、3、4;按行填充需指定 byrow = TRUE。

控制结构​

条件语句​

If-else​

age <- 16
if (age >= 18) {
message("Meets the example age threshold")
} else {
message("Below the example age threshold")
}

Switch​

ch <- "b"
switch(EXPR = ch, a = 1, b = 2:3)

根据 R 控制流规则,if 要求一个非缺失的逻辑结果,不能直接接收 heights > 1.7 这样的向量。逐项筛选用逻辑子集;需要一个整体判断时用 any() 或 all(),并明确如何处理缺失值。& 和 | 逐元素运算;&& 和 || 对标量条件短路求值。字符形式的 switch() 按名称匹配分支;没有匹配且没有默认分支时返回 NULL。

循环结构​

For 循环​

for (i in 1:10) {
print(i)
}

While 循环​

v <- 10
while(v > 2) {
print(v)
v <- v - 1.1
}

Repeat 循环​

i <- 1
repeat {
print(i)
i <- i * 2
if (i > 100) break
}

按下标遍历可能为空的对象时,用 seq_along(x),不要用 1:length(x):长度为零时,1:0 反而产生 1、0 两个数。break 退出最内层循环,next 跳到它的下一次迭代。while 和 repeat 循环必须逐步接近停止条件。

函数与函数式编程​

定义函数​

customMean <- function(x) {
s <- i <- 0
for (j in x) {
s <- s + j
i <- i + 1
}
return(s / i)
}

这个教学版本假设输入是数值向量,每个元素只累加一次。空向量会得到 NaN(0 / 0),NA 会传播到结果;它没有实现 mean() 的选项和数值处理。实际分析应优先用 mean()。省略 return() 时,函数返回最后求值的表达式;打印一个值和返回一个值不是相同的约定。

作用域 (Scope)​

函数中的普通赋值建立局部绑定。若局部找不到某个名称,R 会沿定义该函数时的外围环境查找,这称为词法作用域。下面的 a 来自外围环境,所以 Sum(10) 返回 13;下次调用前修改那个 a,结果也会变化。

a <- 3
Sum <- function(b) {
a + b
}
Sum(10)

高阶函数​

函数可以作为参数传递给其他函数。

f <- function(x, fun) {
fun(x)
}
f(1:10, mean)

包管理​

安装包​

install.packages("ggplot2")
BiocManager::install("maftools")
remotes::install_github("tidyverse/ggplot2")

安装会把包代码下载到库中,与在当前会话中附加包是两件事。Bioconductor 和 GitHub 示例分别要求先安装 BiocManager 和 remotes。它们是不同来源,不是每个项目都要依次执行的三个步骤。应使用项目记录的依赖和可信包源,不要运行来历不明的安装命令。

加载包​

library(ggplot2)

常见陷阱与最佳实践​

复数表示​

使用 i 表示虚部。

1 + 2i

= 与 <- 的区别​

普通赋值用 <-,函数调用中命名参数用 =。customMean(x = 1:100) 中的 x 指函数参数,不会给调用者的变量赋值。下面故意演示另一种行为:传入值的同时给调用者的 x 赋值。这种副作用通常最好拆成单独语句。

x <- NULL
customMean(x <- 1:100)
x

命名空间调用 ::​

package::function() 直接调用包内导出的函数,无需先 library() 加载整个包。

stats::median(1:5)

三冒号 package:::function() 用于访问未导出的内部函数。这些函数可能随版本变化,应用代码不应依赖它们。

因子水平管理​

下面先扩充字符值,再重建因子;这是添加类别,不是重排已有因子的水平。要控制顺序,应显式指定 levels = ...。直接给因子赋一个尚不存在的标签,会产生 NA 并警告,除非先添加该水平。as.numeric(factor) 返回内部水平编码,不是看起来像数字的标签;若标签表示数值,应先转为字符再转数值。

sex <- factor(c(as.character(sex), "M", "M"))
探索关联打开关联网络