使用正则表达式删除 R 数据帧列中的重复元素

Using regex to drop duplicated elements in columns of an R dataframe

我有一个虚拟数据框 df,其尺寸为 6 X 4。

df <- data.frame(
 Hits = c("Hit1", "Hit2", "Hit3", "Hit4", "Hit5", "Hit6"),
 GO = c("GO:0005634~nucleus,", "", "GO:0005737~cytoplasm,", "GO:0005634~nucleus,GO:0005737~cytoplasm,", "", 
            "GO:0005634~nucleus,GO:0005654~nucleoplasm,"),
 KEGG = c("", "", "", "", "", ""),
 SMART = c("SM00394:RIIa,", "SM00394:RIIa,", "", "SM00054:EFh,", 
            "", "SM00394:RIIa,SM00239:C2,"))

df 看起来像这样

列中的元素由两部分组成:

对于每一列,我想保留一行,如果它至少包含一个 term,而该行不存在于它上面的任何行中。例如在 GO 列中,第 1 行和第 3 行包含唯一术语,因此应保留这些术语。第 4 行包含已存在于第 1 行和第 3 行中的术语,因此应将其删除。第 6 行有一项在它上面的任何一行中都不存在,因此它也应该被保留。

我已经能够想出正则表达式来从 GOSMART

列中提取术语
Regex for GO: (?<=~).*?(?=,(?:GO:\d+~|$))
Regex for SMART: (?<=:).*?(?=,(?:\w+\d+:|$)) 

但我无法找到一种方法将正则表达式和上述条件集成到解决方案中。输出应该是这样的

关于如何解决这个问题有什么建议吗?

这是一个通用方法,可以处理 GOSMART 和潜在的 KEGG,但如果没有关于 KEGG 的任何信息就不可能说。

下面的函数f接受参数

  • x,一个字符向量
  • split,列表中分隔项目的分隔符
  • sep,项目中分隔标识符和术语的分隔符

和 returns 一个逻辑向量,用至少一个 non-duplicated 项索引 x 的元素。

f <- function(x, split, sep) {
    l1 <- strsplit(x, split)
    tt <- sub(paste0("^[^", sep, "]*", sep), "", unlist(l1))
    l2 <- relist(duplicated(tt), l1)
    !vapply(l2, all, NA)
}

f应用到GOSMART

nms <- c("GO", "SMART")
l <- Map(f, x = df[nms], split = ",", sep = c("~", ":"))
l
## $GO
## [1]  TRUE FALSE  TRUE FALSE FALSE  TRUE
## 
## $SMART
## [1]  TRUE FALSE FALSE  TRUE FALSE  TRUE

GOSMART""个元素设置为零non-duplicated项,然后过滤掉空行,我们得到想要的结果:

df2 <- df
df2[nms] <- Map(replace, df2[nms], lapply(l, `!`), "")
df2[Reduce(`|`, l), ]
##   Hits                                         GO KEGG                    SMART
## 1 Hit1                        GO:0005634~nucleus,                 SM00394:RIIa,
## 3 Hit3                      GO:0005737~cytoplasm,                              
## 4 Hit4                                                             SM00054:EFh,
## 6 Hit6 GO:0005634~nucleus,GO:0005654~nucleoplasm,      SM00394:RIIa,SM00239:C2,

以下算法应用于每个术语(GO、SMART、KEGG):

  • 将标识符+术语列表提取为comma-separated。参见 stringr::str_split
  • 将术语提取为正则表达式
  • 在出现时沿数据框累积所有项
  • 提取每行与前一行之间的差异
  • 如果没有引入新术语,则将字符串替换为""
  • 过滤并非所有术语都是 ""
  • 的行
library(dplyr)
library(stringr)
library(purrr)

termred <- function(terms, rx) {
  terms |>
    stringr::str_split(",") |>
    purrr::map(stringr::str_trim) |>
    purrr::map(~{.x[.x != ""]}) |>
    purrr::map(~stringr::str_extract(.x, rx)) |>
    purrr::accumulate(union) %>%
    {mapply(setdiff, ., lag(., 1), SIMPLIFY = TRUE)} %>%
    {ifelse(sapply(., length) > 0, terms, "")}
}

df |>
  transform(GO = termred(GO, "~.*$")) |>
  transform(SMART =  termred(SMART, ":.*$")) |>
  filter(GO != "" | SMART != ""| KEGG != "")
##>  Hits                                         GO KEGG                    SMART
##>1 Hit1                        GO:0005634~nucleus,                 SM00394:RIIa,
##>2 Hit3                      GO:0005737~cytoplasm,                              
##>3 Hit4                                                             SM00054:EFh,
##>4 Hit6 GO:0005634~nucleus,GO:0005654~nucleoplasm,      SM00394:RIIa,SM00239:C2,