在R中跨列查找特定字符组合并匹配对应sampleID
R:匹配字符组合对应的样本ID
需求说明
现有两个R数据框:
- df1:存储特定字符组合(实际数据每行的组合元素数量不固定)
- df2:包含样本ID列表及各样本的字符拥有情况
需要为df1的每一行字符组合,找出df2中包含该组合所有字符的样本ID(样本可拥有额外字符,同一样本ID可出现在df1的多行结果中)。
示例数据
df1 <- data.frame(entry1 = c("A","B","C"), entry2 = c("D","E","F"), entry3 = c("G","H","I")) df2 <- data.frame(sampleID = c("1001","1002","1003","1004","1005"), "A" = c("A","0","0","A","A"), "B" = c("B","B","B","0","0"), "C" = c("0","0","0","C","C"), "D" = c("D","0","D","0","0"), "E" = c("E","E","0","0","0"), "F" = c("0","0","0","F","F"), "G" = c("G","0","0","G","0"), "H" = c("H","H","H","H","0"), "I" = c("0","0","I","O","0"))
期望输出
df1.2 <- data.frame(entry1 = c("A","B","C"), entry2 = c("D","E","F"), entry3 = c("G","H","I"), sampleID.1 = c("1001","1001",""), sampleID.2 = c("","1002",""))
解决方案
用dplyr和tidyr包实现高效匹配,避免低效循环,同时兼容df1每行元素数量不固定的场景:
步骤1:预处理df2,生成每个样本的字符集合
先把df2转换为长格式,过滤掉无字符的"0",再为每个样本整理出拥有的字符列表:
library(dplyr) library(tidyr) df2_processed <- df2 %>% pivot_longer(-sampleID, names_to = "char", values_to = "value") %>% filter(value != "0") %>% group_by(sampleID) %>% summarise(chars = list(char)) %>% ungroup()
步骤2:预处理df1,生成每行的需求字符集合
提取df1每行的非空字符(适配元素数量不固定的场景):
df1_processed <- df1 %>% rowwise() %>% mutate( required_chars = list(c_across(everything()) %>% na.omit()) ) %>% ungroup()
步骤3:匹配符合条件的样本ID
对df1的每一行,筛选出df2中字符集合包含所有需求字符的样本:
df1_with_samples <- df1_processed %>% rowwise() %>% mutate( matched_samples = list(df2_processed$sampleID[ sapply(df2_processed$chars, function(x) all(required_chars %in% x)) ]) ) %>% ungroup() %>% select(-required_chars)
步骤4:转换为期望的宽格式
将匹配到的样本ID拆分为单独列,空值填充为空白字符串:
# 确定最多需要多少个样本列 max_sample_cols <- max(sapply(df1_with_samples$matched_samples, length)) # 拆分并重命名列 df1.2 <- df1_with_samples %>% unnest_wider(matched_samples, names_sep = ".") %>% mutate(across(starts_with("matched_samples."), ~ ifelse(is.na(.), "", .))) %>% rename_with(~ gsub("matched_samples.", "sampleID.", .), starts_with("matched_samples.")) # 填充不足的列(如果有的话) for(col_idx in (ncol(df1)+1):(ncol(df1)+max_sample_cols)) { col_name <- paste0("sampleID.", col_idx - ncol(df1)) if(!col_name %in% colnames(df1.2)) { df1.2[[col_name]] <- "" } } # 查看结果 df1.2
运行后即可得到与期望输出一致的结果,同时该方法能高效处理更大规模的数据,也兼容df1每行元素数量不固定的情况。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

