【问题标题】:R - How to classify each row in a dataframe depending on partial matching with another dataframe?R - 如何根据与另一个数据帧的部分匹配对数据帧中的每一行进行分类?
【发布时间】:2018-05-07 21:44:12
【问题描述】:

我有两个数据框(df1 和 df2),这里是 df1:

SAMPLE NAMES
1_a
1_b
1_c
2_a
2_b
3_a
4_a
4_b

这里是df2:

ID  GROUP   
1   X
2   X
3   Y
4   Z

这就是我想要做的 - 我想在 df1 中添加一个新列,这将基于与 df2 的 ID 列的部分匹配来指示样本的组。因此,df1 中的样本“2_a”和“2_b”应该与 df2 中的“2”具有相同的组。

期望的输出:

SAMPLE NAMES    GROUP
1_a             X
1_b             X
1_c             X
2_a             X
2_b             X
3_a             Y
4_a             Z
4_b             Z

到目前为止,我已经尝试使用 stringr 包并编写了一个 for 循环:

for (i in df1[, 1]){
  for (j in df2$ID){
    x <- which(str_detect(i,j))
    class <- df2[j,1]
    df1$group[i] <- class
  }
}

但它一直给我错误:

UseMethod("type") 中的错误: 没有适用于“类型”的方法应用于“c('integer', 'numeric')”类的对象

我做错了什么?另外,有没有办法使用 apply() 函数而不是循环来做到这一点?

【问题讨论】:

    标签: r dataframe stringr


    【解决方案1】:

    这是一个tidyverse 选项

    library(tidyverse)
    df1 %>% 
     separate(., col = SAMPLE.NAMES, into = c('SAMPLE', 'NAMES'), sep = "_", convert = TRUE) %>% 
     left_join(df2, by = c('SAMPLE' = 'ID')) %>% 
     unite(., col = SAMPLE.NAMES, SAMPLE, NAMES)
    #  SAMPLE.NAMES GROUP
    #1          1_a     X
    #2          1_b     X
    #3          1_c     X
    #4          2_a     X
    #5          2_b     X
    #6          3_a     Y
    #7          4_a     Z
    #8          4_b     Z
    

    我们首先将separatedf1 的列“SAMPLE.NAMES”分成两部分,这样我们就可以通过“SAMPLE”和“ID”将left_joindf1df2 结合起来。在最后一行中,我们将unite 列“SAMPLE”和“NAME”返回到“SAMPLE.NAMES”。

    数据

    df1 <- structure(list(SAMPLE.NAMES = structure(1:8, .Label = c("1_a", 
    "1_b", "1_c", "2_a", "2_b", "3_a", "4_a", "4_b"), class = "factor")), .Names = "SAMPLE.NAMES", class = "data.frame", row.names = c(NA, 
    -8L))
    
    df2 <- structure(list(ID = 1:4, GROUP = structure(c(1L, 1L, 2L, 3L), .Label = c("X", 
    "Y", "Z"), class = "factor")), .Names = c("ID", "GROUP"), class = "data.frame", row.names = c(NA, 
    -4L))
    

    【讨论】:

      【解决方案2】:

      您的 for 循环不起作用的主要原因是 str_detect() 仅将字符串作为输入,但您试图在 df2 的 ID 列上使用它,这是一个数字向量。您的 for 循环还有其他问题:特别是,您定义了一个对象 x,该对象之后从未实际使用过,因此您的代码不会使用 str_detect() 选择您想要的元素。

      如果您想要更多 stringr 解决方案,这里还有一个选项。它既不使用 for 循环也不使用 apply()(至少,不直接使用)。

      它的工作原理是使用正则表达式从“SAMPLE.NAMES”列中仅提取数字字符,以将每个样本链接到其数字 ID。之后,我们只需将数据框连接在一起并选择您想要的列:

      # Example dataframes
      df1 <- tibble(SAMPLE.NAMES = c("1_a", "1_b", "1_c", "2_a", "2_b", "3_a", "4_a", "4_b"))
      df2 <- tibble(ID = c(1,2,3,4),
                    GROUP = c("X", "X", "Y", "Z"))
      
      df1 <- mutate(df1, ID = as.numeric(str_replace_all(SAMPLE.NAMES, "_[abc]", ""))) %>%
             left_join(df2) %>%
             select(-ID)
      
      # Output:
      # A tibble: 8 x 2
        SAMPLE.NAMES GROUP
        <chr>        <chr>
      1 1_a          X    
      2 1_b          X    
      3 1_c          X    
      4 2_a          X    
      5 2_b          X    
      6 3_a          Y    
      7 4_a          Z    
      8 4_b          Z  
      

      【讨论】:

        【解决方案3】:

        只需在下划线之前的字符串部分合并:

        > df1$ID <- sub("_.+$","",df1$SAMPLENAMES)
        > df1
          SAMPLENAMES ID
        1         1_a  1
        2         1_b  1
        3         1_c  1
        4         2_a  2
        5         2_b  2
        6         3_a  3
        7         4_a  4
        8         4_b  4
        > merge(df1,df2, by="ID")
          ID SAMPLENAMES GROUP
        1  1         1_a     X
        2  1         1_b     X
        3  1         1_c     X
        4  2         2_a     X
        5  2         2_b     X
        6  3         3_a     Y
        7  4         4_a     Z
        8  4         4_b     Z
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-09-20
          • 2018-03-30
          • 1970-01-01
          • 2021-08-11
          • 1970-01-01
          • 2013-02-24
          • 1970-01-01
          相关资源
          最近更新 更多