缘由

生信之路,道阻且长,记录经验,但渡有缘人。

碎碎念

及时当勉励,岁月不饶人 ——陶渊明

代码

在生信分析中,我们经常会遇到以下问题:

  • 一个字段里塞了多个 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 的好处

  • 固定常用参数
  • 函数更短、更语义化
  • 非常适合写“数据清洗工具函数”