R语言如何移除数据框两列间匹配字符直至仅保留差异字符
R语言实现REF与Effect_allele列共有前缀逐次移除
你需要实现的是基因组等位基因格式化的常见操作:从字符串首位开始逐位比对,移除两列共有的连续前缀,直到某一列被清空、或两列首位字符不再匹配为止,不需要逐次调用gsub,用基础R的向量化操作可以很简洁地实现。
实现代码
基础R版本(无需安装第三方包)
首先定义批量处理共有前缀的函数,直接替换数据框对应列即可:
# 定义移除共有前缀的函数 trim_common_prefix <- function(ref_col, alt_col) { # 逐行拆分字符串为字符向量 ref_split <- strsplit(ref_col, "") alt_split <- strsplit(alt_col, "") # 逐行计算共有前缀的长度 prefix_len <- mapply(function(r, a) { min_len <- min(length(r), length(a)) if (min_len == 0) return(0) # 找第一个不匹配的字符位置 first_diff <- which(r[seq_len(min_len)] != a[seq_len(min_len)])[1] # 全匹配则返回较短字符串的长度,否则返回第一个不匹配位置的前一位 ifelse(is.na(first_diff), min_len, first_diff - 1) }, ref_split, alt_split) # 截取去掉前缀后的字符串 list( REF = substr(ref_col, prefix_len + 1, nchar(ref_col)), Effect_allele = substr(alt_col, prefix_len + 1, nchar(alt_col)) ) } # 替换原数据框列 trim_res <- transform( df, REF = trim_common_prefix(REF, Effect_allele)$REF, Effect_allele = trim_common_prefix(REF, Effect_allele)$Effect_allele )
运行后输出结果和逻辑预期完全一致:
CHROM POS_GRCh38 REF Effect_allele Effect_size 234 chr1 109655507 AAA 0.0266 240 chr1 145830809 T 0.0126 243 chr1 168201814 A 0.0718 244 chr1 172359627 A 0.0655 249 chr1 204533386 TCTGAAACAGGG C 0.1345
注:你给出的预期结果中第四行Effect_allele列的T属于排版空格错位,按比对逻辑该行共同前缀为第一位的T,移除后Effect_allele应为空值。
Tidyverse版本(适合管道操作习惯)
如果平时用dplyr处理数据,可以用更简短的行内操作实现:
library(dplyr) library(stringr) trim_res <- df %>% rowwise() %>% mutate( # 计算共有前缀长度 com_len = min(nchar(REF), nchar(Effect_allele)) %>% seq_len() %>% {which(str_sub(REF, ., .) != str_sub(Effect_allele, ., .))[1] - 1} %>% replace(is.na(.), min(nchar(REF), nchar(Effect_allele))), # 截取字符串 REF = str_sub(REF, com_len + 1), Effect_allele = str_sub(Effect_allele, com_len + 1), # 删除临时列 com_len = NULL ) %>% ungroup()
方案优势
- 不需要提前判断两列字符串长度、手动创建最大值/最小值索引,逻辑更直白
- 向量化运算效率远高于逐行循环、多次调用
gsub的写法,处理十万级以上的GWAS汇总数据速度优势明显 - 不会出现
gsub误匹配字符串中间相同字符的问题,完全符合VCF文件等位基因标准化的左对齐trim规则,如果后续需要移除共有后缀,只要在函数中增加反向逐位匹配的逻辑即可。
内容的提问来源于stack exchange,提问作者Achal Neupane
相关产品推荐
相关产品推荐

