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") 是字符向量,不是数值与文本的混合向量。
数据结构
基本数据类型
- 数值型 (Numeric):包含整数和浮点数
- 字符型 (Character):文本数据
- 逻辑型 (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"))