对包含因子、NA 值和通配符的数据框进行子集化

Subsetting a data frame containing factors, NA values, and wildcards

所以我有一个包含几个不同类别的大数据框,下面是一个简化的示例(真实数据集有 10 多种不同的组织,15 种以上不同的独特细胞类型,每个组织的名称长度可变,以及数千个基因)。组织列的格式为因子。

GENENAME    Tissue1     Tissue2     Tissue3
Gene1       CellType_AA CellType_BB CellType_G
Gene2       CellType_AA CellType_BB       <NA>
Gene3       CellType_AA       <NA>        <NA>
Gene4       CellType_AA CellType_BB CellType_G
Gene5             <NA>        <NA>  CellType_G
Gene6             <NA>  CellType_BB CellType_H
Gene7       CellType_AC CellType_BD CellType_H
Gene8             <NA>        <NA>  CellType_H
Gene9       CellType_AC CellType_BD       <NA>
Gene10            <NA>  CellType_BB       <NA>
Gene11            <NA>  CellType_BD CellType_H
Gene12      CellType_AC       <NA>        <NA>
Gene13            <NA>  CellType_E  CellType_I
Gene14      CellType_F  CellType_E  CellType_I
Gene15      CellType_F  CellType_E        <NA>

我想做的是 return 基于多个组织中存在的 CellTypes 的子集,并在我这样做时忽略不必要的列。此外,我想使用通配符(在下面的示例中,CellType_A*,以便同时选择 CellType_AACellType_AB),并在我仅指定一些时忽略其他列列。我希望该函数可以轻松地重复用于不同的细胞类型组合,因此为每一列添加了一个单独的变量。

为此,我设置了下面的函数,将每个变量的默认值设置为 "*",认为如果我不指定输入,它会将这些列中的任何一个视为有效。

Find_CoEnrich <- function(T1="*", T2="*", T3="*"){
  subset(dataset, 
         grepl(T1, dataset$Tissue1)
         &grepl(T2, dataset$Tissue2)
         &grepl(T3, dataset$Tissue3)
         ,select = GENENAME
  )  
}

但是当我运行函数只在一个列上时,要测试它

Find_CoEnrich(T1="CellType_AA")

它只会return以下内容:

   GENENAME
1     Gene1
4     Gene4

而不是

1     Gene1
2     Gene2
3     Gene3
4     Gene4

跳过在另一列中包含 NA 的任何行。更神秘的是,如果我尝试使用通配符,它​​似乎会忽略字符串的其余部分,而只是 returns 只有那些在每一行中都有值的行,即使它们与字符串的其余部分不匹配,例如 Gene14:

Find_CoEnrich(T1="CellType_A*")

   GENENAME
1     Gene1
4     Gene4
7     Gene7
14   Gene14

我很确定是 table 中 NA 的存在导致了问题,但我花了很长时间试图纠正这个问题并且 运行 失去耐心。如果有人能提供帮助,将不胜感激。

您打算使用的通配符 * 作为正则表达式具有特定含义,这就是您告诉 grepl 接受哪些值的方式 - 它意味着前面的 0 次或多次重复特点。另外,我相信您需要在 grepl 表达式之间进行布尔 OR (|) 运算,因为您需要其中一列与模式匹配的任何行。

这里有一个可能更简单的解决方案,使用 tidyverse,使用单独的 'row-based filtering' 和 'column selection' 步骤:

library(tidyverse)

dataset <-  # small subset of your data, rows 1-4 should match but not 5
  tribble(
    ~GENENAME,    ~Tissue1,     ~Tissue2,     ~Tissue3,
    "Gene1", "CellType_AA", "CellType_BB", "CellType_G",
    "Gene2", "CellType_AA", "CellType_BB", NA,
    "Gene3", "CellType_AA", NA, NA,
    "Gene4", "CellType_AA", "CellType_BB", "CellType_G",
    "Gene5", NA, NA, "CellType_G"
    )

desired_pattern <- "CellType_A"  # note that this already implies that any other character can follow, e.g. this will match CellType_AA, CellType_AB, etc.

dataset %>%
  select(all_of(c("GENENAME","Tissue1","Tissue2","Tissue3"))) %>%  # the column selection
  filter(if_any(  # this is a tad confusing: return the row if any of the specified columns matches the condition...
    .cols = all_of(c("Tissue1", "Tissue2", "Tissue3")),  # specify which columns to check
    .fns = ~ stringr::str_detect(.x, pattern = desired_pattern)  # specify the condition...str_detect() is basically grepl() under the hood
  ))  

要更改为匹配的以 A 或 B 开头的单元格类型,您可以相应地更改模式:

desired_pattern  <- ""  # this will match any cell type that starts with A or B

编辑:

要查找同时匹配其中一列 CellType_A 和另一列 CellType_B 的行,您可以执行两个连续的过滤步骤:

dataset %>%
  select(all_of(c("GENENAME","Tissue1","Tissue2","Tissue3"))) %>%  # the column selection
  filter(if_any(  # in this step, keep only rows that contain at least one `CellType_A`
    .cols = all_of(c("Tissue1", "Tissue2", "Tissue3")),  # specify which columns to check
    .fns = ~ stringr::str_detect(.x, pattern = "CellType_A")
  )) %>%
  filter(if_any(  # in this step, keep only rows that contain at least one `CellType_B`
    .cols = all_of(c("Tissue1", "Tissue2", "Tissue3")),  # specify which columns to check
    .fns = ~ stringr::str_detect(.x, pattern = "CellType_B")
  ))

以上两个过滤步骤的顺序无关紧要(您可以尝试调换顺序说服自己!)