【问题标题】:How to semi_join two dataframes by string column with one being colon-separated如何通过字符串列半连接两个数据帧,其中一个以冒号分隔
【发布时间】:2020-03-09 09:59:24
【问题描述】:

我有两个数据框,dfadfb

dfa <- data.frame(
  gene_name = c("MUC16", "MUC2", "MET", "FAT1", "TERT"),
  id = c(1:5)
)

dfb <- data.frame(
  gene_name = c("MUC1", "MET; BLEP", "MUC21", "FAT", "TERT"),
  id = c(6:10)
)

看起来像这样:

> dfa
  gene_name id
1     MUC16  1
2      MUC2  2
3       MET  3
4      FAT1  4
5      TERT  5

> dfb
  gene_name id
1      MUC1  6
2 MET; BLEP  7
3     MUC21  8
4       FAT  9
5      TERT 10

dfa 是我感兴趣的基因列表:我想将dfb 行保留在它们出现的位置,注意数字(MUC1不是 MUC16)。我的new_df 应该是这样的:

> new_df
  gene_name id
1 MET; BLEP  7
2      TERT 10

我的问题是常规的dplyr::semi_join() 确实匹配,这没有考虑dfb$gene_names 可以包含用"; " 分隔的基因这一事实。这意味着在此示例中,"MET" 不会保留。

我试图调查fuzzyjoin::regex_semi_join,但我不能让它做我想做的事......

欢迎使用 tidyverse 解决方案。 (也许是stringr?!)

编辑:后续问题...

我将如何进行倒数anti_join?在这种方法中简单地将semi_join 更改为anti_join 是行不通的,因为MET; BLEP 行在不应该出现的时候出现了......

anti_join 之后添加filter(gene_name == new_col) 可用于提供的简单数据集,但如果我像这样稍微扭曲一下:

dfa <- data.frame(
  gene_name = c("MUC16", "MUC2", "MET", "FAT1", "TERT"),
  id = c(1:5)
)

dfb <- data.frame(
  gene_name = c("MUC1", "MET; BLEP", "MUC21; BLOUB", "FAT", "TERT"),
  id = c(6:10)
)

...然后就没有了。在这里和我的真实数据集中,dfa 不包含分号,它只是一列单个基因名称。但是dfb包含了很多信息,分号的多种组合...

【问题讨论】:

    标签: r dplyr fuzzyjoin anti-join semi-join


    【解决方案1】:

    我想我终于设法让fuzzyjoin::regex_joins 做我想做的事。这非常简单,我只需要调整我的dfa 过滤器列表:

    library(fuzzyjoin)
    
    # add "\b" regex expression before/after each gene of the list to filtrate from
    # (to search for whole words)
    dfa$gene_name <- paste0("\\b", dfa$gene_name, "\\b")
    
    # to keep genes from dfb that are present in the dfa filter list
    dfb %>% 
      regex_semi_join(dfa, by = c(gene_name = "gene_name"))
    
    # to exclude genes from dfb that are present in the dfa filter blacklist
    dfb %>% 
      regex_anti_join(dfa, by = c(gene_name = "gene_name"))
    

    不过有个缺点:速度很慢...

    【讨论】:

      【解决方案2】:

      您可以在加入前使用seperate_rows() 拆分数据框。请注意,如果 dfa 中存在 BLEP,则会导致重复,这就是使用 distinct 的原因

      dfa <- data.frame(
        gene_name = c("MUC16", "MUC2", "MET", "FAT1", "TERT"),
        id = c(1:5),
        stringsAsFactors = FALSE
      )
      
      dfb <- data.frame(
        gene_name = c("MUC1", "MET; BLEP", "MUC21", "FAT", "TERT"),
        id = c(6:10),
        stringsAsFactors = FALSE
      )
      
      
      library(tidyverse)
      
      dfb%>%
        mutate(new_col = gene_name)%>%
        separate_rows(new_col,sep = "; ")%>%
        semi_join(dfa,by = c("new_col" = "gene_name"))%>%
        select(gene_name,id)%>%
        distinct()
      
      
      

      【讨论】:

      • 这也可以完成工作... separate_rows(new_col,sep = "; ") %&gt;% filter(new_col %in% dfa$gene_name) %&gt;% ... 而不是加入。
      • 感谢@Sada93 和@AntoniosK,您的回答都有效! @Sada93,如果您将separate_rows(new_col,sep = ";")%&gt;% mutate(new_col = gsub(" ","",new_col))%&gt;% 修改为仅separate_rows(new_col,sep = "; ")%&gt;%,我将很高兴接受您的回答!
      • 后续问题:我将如何进行倒数anti_join?在这种方法中简单地将semi_join 更改为anti_join 是行不通的,因为MET; BLEP 行在不应该出现的时候出现了...... @AntoniosK
      • 您可以在anti_join 之后添加filter(gene_name == new_col),但我认为这只有在dfa 不使用分号表示时才有效。如果 dfa 也可以包含分号,我们需要一些更高级的东西。
      • 它适用于提供的简单数据集,但如果我像这样稍微扭曲它:dfa &lt;- data.frame( gene_name = c("MUC16", "MUC2", "MET", "FAT1", "TERT"), id = c(1:5) ) dfb &lt;- data.frame( gene_name = c("MUC1", "MET; BLEP", "MUC21; BLOUB", "FAT", "TERT"), id = c(6:10) ) 那么它就不再适用了。在这里和我的真实数据集中,dfa 不包含分号,它只是一列单个基因名称。但是dfb包含很多信息,分号的多种组合...
      【解决方案3】:

      这是使用stringrpurrr 的解决方案。

      library(tidyverse)
      
      dfb %>%
       mutate(gene_name_list = str_split(gene_name, "; ")) %>%
       mutate(gene_of_interest = map_lgl(gene_name_list, some, ~ . %in% dfa$gene_name)) %>%
       filter(gene_of_interest == TRUE) %>%
       select(gene_name, id)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-11-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-05
        • 2020-02-13
        相关资源
        最近更新 更多