R基础回顾(一):向量、矩阵、列表、数据框
咱们正式开始动手前,得先把R的几种核心数据结构捋清楚。别担心,不是枯燥的理论课。我自己的经验是,这几种结构搞明白了,后面写Shiny app的时候,数据怎么流转、怎么变形,心里就有底了。
说白了,R里数据就四种基本容器:向量、矩阵、列表、数据框。它们各有各的脾气,选对了事半功倍。
向量 —— 最基础的单元
向量是R里最基本的对象。就是个一维的同类型元素集合。记住,是同类型。数值、字符、逻辑,混一起会自动转换,这个坑我踩过。
创建向量用 c() 函数,全称是 combine。
# 数值向量
my_numbers <- c(1, 3, 5, 7, 9)
# 字符向量
my_genes <- c("TP53", "BRCA1", "EGFR")
# 逻辑向量
my_flags <- c(TRUE, FALSE, TRUE)
# 向量运算
my_numbers * 2 # 所有元素乘以2
sqrt(my_numbers) # 对每个元素开平方
exp_values <- c(TP53 = 12.5, BRCA1 = 8.3, EGFR = 0.5)
exp_values["TP53"] # 按名字取,清晰多了
矩阵 —— 二维的同类型表格
矩阵其实就是一个加了维度属性的向量。它要求所有元素类型一致,这在处理表达矩阵时很常见。
创建方式有两种,我常用 matrix()。
# 创建一个3行4列的矩阵
mat <- matrix(1:12, nrow = 3, ncol = 4)
mat
# 指定是按行填充还是按列填充
mat_byrow <- matrix(1:12, nrow = 3, byrow = TRUE)
# 给行列加名字
rownames(mat) <- c("GeneA", "GeneB", "GeneC")
colnames(mat) <- c("Sample1", "Sample2", "Sample3", "Sample4")
矩阵运算在差异分析里经常用到。比如计算行均值、列均值。
rowMeans(mat) # 每个基因的平均表达量
colMeans(mat) # 每个样本的平均表达量
rownames() 核对一遍。
列表 —— 灵活的大杂烩
列表是R里最灵活的数据结构。它允许你往一个对象里塞不同类型、不同长度、甚至不同维度的东西。你想想看,这不就是为复杂数据准备的嘛。
# 创建列表
my_list <- list(
gene_name = "TP53",
expression = c(12.5, 8.3, 0.5, 6.7),
is_significant = TRUE,
p_value = 0.003
)
# 访问列表元素(两种方式)
my_list$expression
my_list[["expression"]]
差异分析的结果,比如 DESeq2 输出的对象,本质上就是一个大列表。里面装着结果表、归一化计数、模型拟合信息等等。
[] 和 [[]] 取列表元素,结果可不一样。[] 返回的还是列表,[[]] 才取出里面的实际数据。别搞混了。
数据框 —— 最常用的结构
这才是咱们的日常。数据框看起来像矩阵,但每列可以是不同类型。你在Excel里看到的表,到R里就是数据框。
咱们做差异分析,结果表就是一个数据框。基因名是字符列,log2FC是数值列,p值也是数值列,还有判断是否显著的逻辑列。
# 创建数据框
de_results <- data.frame(
gene = c("TP53", "BRCA1", "EGFR", "MYC"),
log2FC = c(2.5, -1.3, 0.8, 3.1),
pvalue = c(0.001, 0.04, 0.12, 0.0005),
significant = c(TRUE, TRUE, FALSE, TRUE)
)
# 查看结构
str(de_results)
# 提取列
de_results$log2FC
de_results[, "log2FC"]
# 筛选行
de_results[de_results$pvalue < 0.05, ]
讲到这里,你可能已经发现了一个规律:数据框里的每一列,本质上都是一个向量。这才有了列操作的一致性。
四种结构的选用逻辑
| 数据结构 | 维度 | 元素类型 | 适用场景 |
|---|---|---|---|
| 向量 | 一维 | 同类型 | 单个样本的表达值、p值列表、基因名列表 |
| 矩阵 | 二维 | 同类型 | 表达矩阵(所有值都是数值) |
| 列表 | 递归 | 可混合 | DEseq2/limma 输出对象、复杂分析结果 |
| 数据框 | 二维 | 每列同类型 | 差异表达结果表、样本元数据、日常数据分析 |
清楚了这些差别,你在写Shiny app时就知道:传参用列表、建图表用数据框、存表达值用矩阵。各司其职,代码才清爽。
实际项目中怎么选?
我举个例子。你在做差异表达分析,流程大概是:
- 原始数据读进来,是个数据框(因为列类型不同:样本名是字符,表达值是数值)。
- 提取表达值那几列,变成矩阵(方便做数学运算,比如log2转换)。
- 跑差异分析(比如DESeq2),结果是个列表(里面装着各种中间结果、统计检验输出)。
- 把最终结果表(基因、log2FC、p值等)又转回数据框(方便在Shiny里展示和筛选)。
你看,一圈下来,四种结构都用上了。你想想看,如果在第三步你非要把列表当数据框用,代码是不是就拧巴了?
as.matrix(),矩阵转数据框用 as.data.frame()。但是注意,字符列会强制转换整个矩阵为字符类型,别把数值弄丢了。
嗯,到这里核心内容差不多了。这些基础结构是你做差异分析、搭Shiny界面的地基。多操练几遍,写代码时手自然就顺了。