缘由
生信之路,道阻且长,记录经验,但渡有缘人。
碎碎念
及时当勉励,岁月不饶人 ——陶渊明
代码
在数据分析中,我们经常需要 “按组计算” ——
比如蛋白组学中:
对每个蛋白 (Protein),再按每个肽段 (Peptide) 分组,
计算每个实验组 (group) 的平均表达量。
这时我们可以选择三种方式:
1️⃣ split() —— base R 风格
2️⃣ nest() + map() —— tidyverse 风格
3️⃣ group_modify() —— tidyverse 极简风格
示例数据
library(tidyverse)
set.seed(123)
df <- tibble(
Protein = rep(c("P1", "P2"), each = 6),
Peptide = rep(c("pepA", "pepB"), each = 3, times = 2),
group = rep(c("WT", "KO", "AAV"), times = 4),
value = rnorm(12, 10, 1)
)
df一、Base R 风格:split() + lapply()
protein_split <- split(df, df$Protein)
pep_split <- lapply(protein_split, function(x) split(x, x$Peptide))
result_base <- lapply(pep_split, function(protein_list) {
lapply(protein_list, function(pep_df) {
pep_df %>%
group_by(group) %>%
summarise(mean_val = mean(value))
})
})优点: 逻辑清晰、兼容性强
缺点: 嵌套层级多,合并结果麻烦,不易在管道中复用。
二、Tidyverse 风格:nest() + map()
df_nested <- df %>%
group_by(Protein, Peptide, group) %>%
nest()
现在,我们可以在 map() 中对每个小表计算:
df_nested %>%
mutate(res = map(data, ~ summarise(.x, mean_val = mean(value)))) %>%
select(-data) %>%
unnest(res)输出
| Protein | Peptide | group | mean_val |
|---|---|---|---|
| P1 | pepA | WT | 9.44 |
| P1 | pepA | KO | 10.82 |
| P1 | pepA | AAV | 9.69 |
| … | … | … | … |
优点:
- 数据结构更 tidy;
- 用
unnest()一步合并; - 可轻松扩展更复杂计算。
三、Tidyverse 极简风格:group_modify()
group_modify() 是 dplyr 的内置“分组 apply”工具,能自动完成拆分、计算和合并:
df_result <- df %>%
group_by(Protein, Peptide, group) %>%
group_modify(~ summarise(.x, mean_val = mean(value)))
df_result优点:
- 一步到位;
- 自动合并;
- 完全兼容管道风格;
- 支持返回多行或多列结果。