r-ggplot2
速答
Q: 生信分析必须学 R 吗? A: 不是必须,但强烈推荐。Seurat(单细胞 )、DESeq2(差异分析)、clusterProfiler(富集)等核心包都是 R 生态。Python 有 Scanpy 等替代,但 R 在统计可视化和 Bioconductor 生态上仍占优。
Q: ggplot2 的图层语法核心是什么?
A: data + aesthetic + geometry 三件套。数据映射到美学属性(x/y/color),再叠加几何图层(点/线/柱)。图层之间用 + 连接,可以无限叠加。
Q: dplyr 最常用的 5 个函数?
A: filter()(筛选行)、select()(选列)、mutate()(新增列)、group_by() + summarize()(分组汇总)、arrange()(排序)。覆盖 90% 日常数据整理需求。
R 数据整理与 ggplot2 可视化
R 在组学分析中最常用的场景有两个:整理表格数据,画出能解释结果的图。对 BioF3 来说,本章不是追求完整覆盖 R 语言,而是让你掌握最常用、最容易迁移到真实项目的能力。
本章所有图表都来自公开数据集 10x Genomics PBMC 3k。它包含一名健康供体外周血单个核细胞的真实单细胞 RNA-seq 表达矩阵。
R 在 BioF3 中承担什么角色
R 不是唯一选择,但它在生信里非常重要:
- Seurat 是单细胞分析的主流工具之一
- Bioconductor 提供大量组学分析包
- ggplot2 和 ComplexHeatmap 适合发表级可视化
- R Markdown / Quarto 适合生成分析报告
学习 R 的重点不是背语法,而是知道数据对象长什么样、函数需要什么输入、结果如何保存。
准备真实 PBMC 3k 数据
先读取真实表达矩阵。完整脚本会自动下载数据;这里保留核心逻辑,方便你理解后续对象从哪里来。
library(Matrix)
library(dplyr)
library(tidyr)
library(ggplot2)
library(scales)
data_dir <- file.path(path.expand("~"), "biof3-data", "pbmc3k")
matrix_dir <- file.path(data_dir, "filtered_gene_bc_matrices", "hg19")
pbmc_url <- paste0(
"https://cf.10xgenomics.com/samples/cell-exp/1.1.0/pbmc3k/",
"pbmc3k_filtered_gene_bc_matrices.tar.gz"
)
pbmc_tar <- file.path(data_dir, "pbmc3k_filtered_gene_bc_matrices.tar.gz")
dir.create(data_dir, recursive = TRUE, showWarnings = FALSE)
if (!file.exists(pbmc_tar)) {
download.file(pbmc_url, destfile = pbmc_tar, mode = "wb")
}
if (!file.exists(file.path(matrix_dir, "matrix.mtx"))) {
untar(pbmc_tar, exdir = data_dir)
}
counts <- readMM(file.path(matrix_dir, "matrix.mtx"))
genes <- read.delim(file.path(matrix_dir, "genes.tsv"), header = FALSE)
barcodes <- read.delim(file.path(matrix_dir, "barcodes.tsv"), header = FALSE)
rownames(counts) <- make.unique(genes[[2]])
colnames(counts) <- barcodes[[1]]
counts <- as(counts, "CsparseMatrix")
把矩阵整理成几张常用表:
mt_genes <- grepl("^MT-", rownames(counts))
qc <- data.frame(
cell = colnames(counts),
nCount_RNA = as.numeric(colSums(counts)),
nFeature_RNA = as.numeric(colSums(counts > 0)),
percent_mt = as.numeric(colSums(counts[mt_genes, , drop = FALSE]) / colSums(counts) * 100)
)
gene_summary <- data.frame(
gene = rownames(counts),
total_counts = as.numeric(rowSums(counts)),
detected_cells = as.numeric(rowSums(counts > 0))
) %>%
mutate(mean_counts = total_counts / ncol(counts))
marker_genes <- c("IL7R", "CCR7", "S100A8", "S100A9", "MS4A1", "CD79A", "NKG7", "GNLY", "PPBP")
marker_genes <- marker_genes[marker_genes %in% rownames(counts)]
marker_long <- bind_rows(lapply(marker_genes, function(gene) {
data.frame(
cell = colnames(counts),
gene = gene,
counts = as.numeric(counts[gene, ]),
log_counts = log1p(as.numeric(counts[gene, ]))
)
}))
这几张表分别回答不同问题:
counts:基因 x 细胞的稀疏表达矩阵qc:每 个细胞的总 UMI、检测基因数、线粒体比例gene_summary:每个基因的总表达量和检出细胞数marker_long:常见 PBMC marker 基因的长表表达量
最小 R 基础
向量
向量是 R 中最基础的数据结构。这里的基因名来自 PBMC 3k 矩阵中真实存在的 marker 基因。
genes <- marker_genes[1:4]
expression <- gene_summary$total_counts[match(genes, gene_summary$gene)]
genes[1]
expression > median(expression)
mean(expression)
数据框
数据框类似表格,是生信分析中最常见的数据形态。
gene_data <- gene_summary %>%
filter(gene %in% marker_genes) %>%
select(gene, total_counts, detected_cells, mean_counts)
head(gene_data)
str(gene_data)
summary(gene_data)
访问列:
gene_data$gene
gene_data[["total_counts"]]
筛选行:
high_detection <- gene_data[gene_data$detected_cells > 100, ]
列表
很多 R 包会把复杂结果放在列表里。Seurat 对象、差异分析结果和富集分析结果都可能包含多层信息。
analysis_result <- list(
counts = counts,
qc = qc,
marker_expression = marker_long
)
analysis_result$qc
dplyr:表格整理的基本动作
先加载包:
library(dplyr)
用真实基因汇总表练习筛选、排序和新增列:
top_genes <- gene_summary %>%
filter(!grepl("^MT-|^RPL|^RPS", gene)) %>%
filter(detected_cells > 50) %>%
arrange(desc(total_counts)) %>%
mutate(
log_total_counts = log10(total_counts + 1),
detection_rate = detected_cells / ncol(counts)
) %>%
slice_head(n = 10)
分组汇总可以用在 marker 基因上。例如按基因统计非零表达细胞比例:
marker_summary <- marker_long %>%
group_by(gene) %>%
summarise(
mean_log_counts = mean(log_counts),
expressing_cells = sum(counts > 0),
expressing_rate = expressing_cells / n(),
.groups = "drop"
) %>%
arrange(desc(expressing_rate))
在真实项目中,大部分绘图问题都先是数据整理问题。图画不好,常常不是 ggplot2 不会用,而是表格没有整理成合适的长格式。
ggplot2 的核心思想
ggplot2 使用"图形语法"。你可以把一张图理解成几层:
- data:使用哪个数据框
- aes:哪些列映射到 x、y、颜色、形状、大小
- geom:用什么几何对象展示数据
- scale:坐标轴和颜色如何转换
- theme:图的外观
- labs:标题、坐标轴和图例文字
最小示例:
ggplot(top_genes, aes(x = reorder(gene, total_counts), y = total_counts)) +
geom_col(fill = "#0f766e") +
coord_flip() +
scale_y_continuous(labels = comma) +
labs(x = NULL, y = "Total UMI counts") +
theme_classic()

图 1:柱状图展示 PBMC 3k 中总 UMI 数最高的一组真实基因。这里过滤了线粒体和核糖体基因,避免它们占据整个图。