2. R语言基础:数据类型、向量、矩阵、列表、数据框

好了,咱们正式开始动手。这一章,我带你捋一捋R语言的核心数据结构。说白了,这就是你后续所有操作的“地基”。

你可能会问:“为什么非要分清这些?不就是装数据吗?” 嗯,我刚开始用R时也这么想。直到有一次做药物靶点筛选,我把字符型向量直接丢进运算里,R给我吐了一堆NA。排查了一下午,最后发现是数据类型没对齐。所以,咱们从一开始就把它弄明白,省得后面踩坑。

2.1 数据类型:R里的“元素”长什么样

R里最基础的单位是“原子向量”,也就是单个元素。常见的有这么几类:

类型 示例 检查函数 说明
数值型 3.14, -2, 1e5 is.numeric() 默认双精度浮点数,整数也属于数值型
整型 1L, 42L is.integer() 加 L 后缀明确声明为整数
字符型 "aspirin", 'drug' is.character() 用双引号或单引号包裹
逻辑型 TRUE, FALSE is.logical() 比较运算的结果就是它
复数型 1 + 2i is.complex() 做频谱分析时才用,药物数据里很少见

我个人习惯拿到新数据后,先用 str() 扫描一遍结构,看一眼每个字段的类型。它能帮你提前发现那些“看起来是数字,实际上是字符”的坑。

# 快速检查数据类型
x <- 3.14
y <- "ibuprofen"
z <- TRUE

str(x)  # 输出: num 3.14
str(y)  # 输出: chr "ibuprofen"
str(z)  # 输出: logi TRUE

2.2 向量:最常用的“一维容器”

向量是R里最基础的数据结构。你可以把它想象成一列同类型元素的集合。

核心规则: 向量里所有元素必须是同一种类型。如果混入不同类型,R会自动做“强制转换”。比如 c(1, "a"),数字1会被悄悄转成字符"1"。

2.2.1 创建与索引

# 创建向量
drug_names <- c("aspirin", "ibuprofen", "acetaminophen")
dose <- c(100, 200, 500)
is_active <- c(TRUE, FALSE, TRUE)

# 索引(R的索引从1开始,这点和Python不一样)
dose[1]    # 100
dose[c(1, 3)]  # 100, 500
dose[-2]   # 去掉第二个元素,返回 100, 500

# 逻辑索引,工作中最常用
dose[dose > 150]  # 返回 200, 500

你想想看,逻辑索引有多方便?筛选出那些满足条件的药物剂量,一行代码搞定。

2.2.2 向量化运算

R的向量化运算是我最喜欢的功能。不用写循环,直接对整个向量做运算。

dose_mg <- c(100, 200, 500)
dose_g <- dose_mg / 1000  # 返回 c(0.1, 0.2, 0.5)

# 字符串拼接
paste("drug", 1:3, sep = "_")  # "drug_1" "drug_2" "drug_3"

2.3 矩阵:二维的“同质”表格

矩阵就是二维的向量。所有元素类型必须一致。做矩阵乘法、特征分解时用得比较多。

# 创建 3 行 4 列的矩阵,默认按列填充
m <- matrix(1:12, nrow = 3, ncol = 4)
rownames(m) <- c("drug_A", "drug_B", "drug_C")
colnames(m) <- c("site_1", "site_2", "site_3", "site_4")

m
#        site_1 site_2 site_3 site_4
# drug_A      1      4      7     10
# drug_B      2      5      8     11
# drug_C      3      6      9     12

# 提取第2行第3列
m[2, 3]  # 8
注意: 我在做药物-靶点互作矩阵时,曾不小心把字符和数字混在一个矩阵里。结果所有数值都被转成了字符串,后续的计算全乱了。矩阵只适合存单一类型的数据,如果混合类型,请用数据框。

2.4 列表:最能装的“万能口袋”

列表可以装任何东西——向量、矩阵、数据框,甚至是另一个列表。非常灵活,但用起来需要小心。

# 创建一个列表
patient_info <- list(
    id = "P001",
    drugs = c("aspirin", "ibuprofen"),
    dosage = c(100, 200),
    notes = "暂无不良反应"
)

# 提取元素:用 [[]] 或 $
patient_info$id          # "P001"
patient_info[[2]]        # c("aspirin", "ibuprofen")
patient_info[["drugs"]]  # c("aspirin", "ibuprofen")

我这里经常用列表来封装一个药物的完整信息——名称、靶点、剂量、副作用,全都放在一个对象里。做复杂分析时非常方便。

2.5 数据框:药物知识图谱的主力

数据框是矩阵的升级版。每一列可以是不同的类型。说白了,它就像Excel里的表格。做药物知识图谱分析,数据框是绝对的主角。

# 创建数据框
drug_db <- data.frame(
    drug_name = c("aspirin", "ibuprofen", "acetaminophen"),
    target = c("COX1", "COX2", "COX3"),
    half_life = c(0.25, 2.0, 2.5),
    is_otc = c(TRUE, TRUE, TRUE),
    stringsAsFactors = FALSE  # 让字符串保持字符类型
)

str(drug_db)
# 'data.frame':   3 obs. of  4 variables:
#  $ drug_name : chr  "aspirin" "ibuprofen" ...
#  $ target    : chr  "COX1" "COX2" ...
#  $ half_life : num  0.25 2 2.5
#  $ is_otc    : logi  TRUE TRUE TRUE
关键操作:
  • 用 drug_db$drug_name 提取列
  • 用 drug_db[drug_db$half_life > 1, ] 筛选行
  • 用 subset(drug_db, target == "COX2") 条件筛选

2.6 一张图理清关系

下面这张SVG图,帮你把这一章的结构串起来。从上到下看,从基础数据类型到容器递进,非常清晰。

R语言基础数据结构 基础数据类型 一维容器 二维容器 通用容器 向量 同类型元素 矩阵 同类型二维 数据框 异构二维 列表 任意组合 实际应用建议 • 数据框:日常分析和建模的主力,推荐优先使用 • 列表:存储非结构化的药物信息,非常灵活 • 向量:用于单维数据,配合循环和筛选很高效

2.7 避坑指南 & 核心技巧

  • 类型检查:拿到数据先跑 str() 和 class()。我曾经吃过字符型当数值用的亏。
  • 强制转换:R 的转换规则是:逻辑 < 整数 < 数值 < 字符。低级别向高级别自动转。
  • 索引效率:用逻辑向量做索引,比写循环快得多。数据量大时差距明显。
  • 数据框选列:能用 $ 就用 $,可读性最好。如果列名有空格,用反引号包裹 `列名`。
  • 列表递归:提取列表元素时 [] 返回子列表,[[]] 去掉一层包装。这个区别,我大概花了半小时才彻底理解。
一句话总结: 向量是基础,数据框是核心,列表做扩展。做个药物知识图谱分析,80% 的操作都在数据框上。把这一章练好,后面讲 tidyverse 和 dplyr 时,你会感觉非常顺畅。