缘由
生信之路,道阻且长,记录经验,但渡有缘人。
碎碎念
及时当勉励,岁月不饶人 ——陶渊明
代码
在生信分析中,我们经常会遇到以下问题:
- 一个字段里塞了多个 ID
- 需要按 e-value 取最优比对结果
- 富集分析结果里的比例是字符串
- 重复代码太多,不好维护
本文用一个完整示例,展示如何用 tidyverse + purrr 把这些问题一次解决。
一、复杂字符串的优雅拆解
原始数据格式
string1 <- c("uniprot|A|A1;uniprot|B|B1")
string2 <- c("uniprot|C|C1")目标:
👉 提取每个 | 分隔后的 第二个字段,并用 ; 重新拼接。
封装成函数
library(tidyverse)
format_name <- function(str1) {
str1 %>%
str_split(";") %>%
unlist() %>%
map_chr(~ str_split(.x, "\\|")[[1]][2]) %>%
paste(collapse = ";")
}使用效果
format_name(string1)
# [1] "A;B"
format_name(string2)
# [1] "C"📌 技巧总结
str_split():拆字符串map_chr():函数式遍历paste(collapse = ";"):重新拼接
二、按 e-value 选取最优比对结果
每个 query 只保留 e-value 最小的比对
df_min <- df %>%
group_by(qseqid) %>%
slice_min(evalue, n = 1, with_ties = TRUE) %>%
ungroup()📌 这里用到了:
group_by():按 query 分组slice_min():取最小值(支持并列)with_ties = TRUE:保留多个最优结果
三、批量重命名并生成新变量
假设你有一个定量矩阵 quant_data,样本名需要统一重命名并生成强度列。
library(dplyr)
# 创建示例数据
set.seed(123)
samples <- c("Sample_A", "Sample_B", "Sample_C", "Sample_D")
# 创建quant_data数据框
quant_data <- data.frame(
old_name1 = paste0("GENE", 1:10),
old_name2 = paste0("PROT", 1:10),
old_name3 = paste0("Protein involved in ", sample(c("metabolism", "signaling", "transport"), 10, replace = TRUE)),
Sample_A = rnorm(10, 1000, 200),
Sample_B = rnorm(10, 1200, 250),
Sample_C = rnorm(10, 800, 150),
Sample_D = rnorm(10, 1500, 300),
stringsAsFactors = FALSE
)
# 2. 批量生成新列(在原始列的基础上创建_Intensity列)
quant_data <- quant_data %>%
mutate(
across(all_of(samples), ~ .x, .names = "{.col}_Intensity")
)
print("添加_Intensity列后的数据:")
print(quant_data)
# 查看列名
print("当前列名:")
colnames(quant_data)
# 3. 调整列顺序:将_Intensity列移到原始列之前
quant_data <- quant_data %>%
relocate(ends_with("_Intensity"), .before = all_of(samples))
print("调整列顺序后的数据:")
print(quant_data)
print("最终列顺序:")
colnames(quant_data)
四、富集分析比例字段的数值化
富集结果中常见这种字段:
enrich_result <- data.frame(
ID = c("GO:001", "GO:002", "GO:003"),
Description = c("process A", "process B", "process C"),
GeneRatio = c("5/100", "10/150", "3/80"),
BgRatio = c("50/1000", "100/2000", "30/1500"),
pvalue = c(0.001, 0.01, 0.05),
qvalue = c(0.005, 0.02, 0.06),
geneID = c("gene1,gene2,gene3", "gene4,gene5", "gene6,gene7,gene8,gene9"),
Count = c(5, 10, 3),
stringsAsFactors = FALSE
)
tidyverse 标准处理流程
library(tidyr)
res <- enrich_result %>%
separate(GeneRatio, into = c("gr", "gr_den"), sep = "/", convert = TRUE) %>%
separate(BgRatio, into = c("br", "br_den"), sep = "/", convert = TRUE) %>%
mutate(
gene_ratio = round(gr / br, 2)
) %>%
select(-gr_den, -br_den)📌 convert = TRUE 可以自动转为数值型,非常省心。
五、进阶技巧:用 partial() 减少重复代码 ⭐
当你发现自己反复写相同参数的函数时,就该用 partial() 了。
library(purrr)
# 示例函数
power <- function(base, exponent) {
base ^ exponent
}
# 示例数据
numbers <- c(2, 3, 4, 5)
# 固定exponent参数,创建平方函数
square <- partial(power, exponent = 2)
square(5) # 25
square(10) # 100
📌 partial 的好处
- 固定常用参数
- 函数更短、更语义化
- 非常适合写“数据清洗工具函数”