Dplyr 语法选择列并将它们转换为单个列表

Dplyr syntax selecting columns and converting them to a single list

我开始学习如何使用 dplyr 的管道 (%>%) 命令来操作数据帧。我喜欢它看起来更加流线型。然而,我刚遇到一个问题,我只用管道无法解决。

我有一个包含关系(网络)数据的数据框,如下所示:

前两列表示哪些项目(基因)之间存在关系,第三列包含有关该关系的信息:

  a      b      c    
1 Gene_1 Gene_2 X    
2 Gene_2 Gene_3 R    
3 Gene_1 Gene_4 X    

我的目标是获得具有相同属性的独特基因列表。如果选择第 3 列中的属性 X,我会得到这个数据框:

  a      b      c    
1 Gene_1 Gene_2 X     
3 Gene_1 Gene_4 X   

我想以这份独特基因列表结束:

genes = c("Gene_1" "Gene_2" "Gene_4")

项目(基因)来自第一列还是第二列都没有关系,我只想要一个唯一的列表。我想到了这个解决方案:

library(tidyr)

net = tibble(a = c("Gene_1", "Gene_2", "Gene_1"),
       b = c("Gene_2", "Gene_3", "Gene_4"),
       c = c("X", "R", "X"))

df = net %>% 
  filter(c == "X") %>%
  select(c(1,2)) 

genes = unique(c(df$a, df$b))

但我不满意,因为我无法在 dplyr 管道命令中执行所有操作。我必须在管道命令之外创建一个列表,然后对其调用 unique。

有没有办法通过调用另一个管道来完成这个任务?无论如何我找不到这样做。谢谢。

1) 像这样使用 {...}:

net %>% 
  filter(c == "X") %>%
  select(c(1,2)) %>%
  { unique(c(.$a, .$b)) }
## [1] "Gene_1" "Gene_3" "Gene_2" "Gene_5"

2) 或使用 magrittr 的 %$% 管道:

library(magrittr)

net %>% 
  filter(c == "X") %>%
  select(c(1,2)) %$%
  unique(c(a, b))
## [1] "Gene_1" "Gene_3" "Gene_2" "Gene_5"

3) 或使用 with:

net %>% 
  filter(c == "X") %>%
  select(c(1,2)) %>%
  with(unique(c(a, b)))
## [1] "Gene_1" "Gene_3" "Gene_2" "Gene_5"

由于结果不是数据框最好不要调用它df.

我建议使用 tidyr::pivot_longer 将来自两个不同基因列的潜在匹配的多列重塑为值列(我们关心的)和名称列(引用原始列名称,我们不关心并且可以忽略)。然后 distinct 获得唯一匹配,最后匹配到列 c:

net %>%
  pivot_longer(-c) %>%
  distinct(c, value) %>%
  filter(c == "X") 

如果您希望结果作为向量,您可以添加 %>% pull(value)

这种方法的一个好处是我们已经为每一列计算了每组不同的基因 c 值,最后 filter 步骤只是将它缩小到一个例子 c值。

结果

  c     value 
  <chr> <chr> 
1 X     Gene_1
2 X     Gene_2
3 X     Gene_4

[注意:我制作了 a = c("Gene_1", "Gene_2", "Gene_1")b = c("Gene_2", "Gene_3", "Gene_4") 来匹配示例。]

我知道这个问题有几个答案,但我会用稍微不同的方式来回答它。也许对某人有用?

我也创建了一个数据集来演示。

library(tidyverse) 
library(stringi)    # only used in data generation

# data set creation 100 rows
a = paste0("Gene_",1:100)
b = paste0("Gene_",round(runif(100, 10, 99),digits = 0))
cC = paste0(stringi::stri_rand_strings(100, 1, '[A-Z]'))

# put it together and strip the information    
data.frame(a = a, b = b, cC = cC) %>% # collect the data
   filter(cC == "X") %>%              # filter for attribute
   select(-cC) %>%                    # remove attribute field
   unlist() %>%                       # collapse the data frame into a vector
   unique()                           # show me what's unique

# output example
# [1] "Gene_10" "Gene_12" "Gene_28" "Gene_77" "Gene_22" "Gene_41" "Gene_75"
# [8] "Gene_19"

unlist() 函数可能就是您要找的。

引用 ?unlist 的内置文档:“给定一个列表结构 x,unlist 将其简化为生成一个包含 x 中出现的所有原子组件的向量。”。 =29=]

由于 R 数据帧(和 tibbles)是作为长度相等的列向量列表实现的,unlist 函数将有效地将数据帧转换为向量。

使用 filterselect 对所需的行和列进行子集化,然后将结果通过 unlist()unique() 进行管道传输。结果将是一个包含不同元素的向量。

library(dplyr)

# The example data
tibble(a = c("Gene_1", "Gene_2", "Gene_1"),
       b = c("Gene_2", "Gene_3", "Gene_4"),
       c = c("X", "R", "X")) %>%
    
    # Subset data for desired feature
    filter(c == "X") %>%
    
    # Select identifier columns
    select(a, b) %>%
    
    # convert to a vector
    unlist() %>%
    
    # derive unique elements
    unique()

结果

[1] "Gene_1" "Gene_2" "Gene_4"
library(tidyverse)

net <- tibble(
  a = c("Gene_1", "Gene_1", "Gene_3"),
  b = c("Gene_2", "Gene_4", "Gene_5"),
  c = c("X", "R", "X")
)

df <- net %>%
  filter(c == "X") %>%
  select(a, b)
df
#> # A tibble: 2 x 2
#>   a      b     
#>   <chr>  <chr> 
#> 1 Gene_1 Gene_2
#> 2 Gene_3 Gene_5

genes <- net %>%
  select(-c) %>%
  unlist() %>%
  unique()
genes
#> [1] "Gene_1" "Gene_3" "Gene_2" "Gene_4" "Gene_5"

尽管 OP 也提出并接受了许多有启发性的答案,但我只想补充一点,以防万一,您希望它同时用于 c 中的所有值,请执行此操作

library(tidyverse)

net %>%
  group_split(c, .keep = F) %>%
  setNames(unique(net$c)) %>%
  map(~ (.x %>% unlist() %>% unique()))

$X
[1] "Gene_2" "Gene_3"

$R
[1] "Gene_1" "Gene_2" "Gene_4"