【问题标题】:Matching multiple strings and join匹配多个字符串并加入
【发布时间】:2019-01-31 08:14:48
【问题描述】:

我在匹配 r 中的多个字符串时遇到了困难。我拥有的数据框如下所示:

      Var1                                      Var2
1   SJDJWK   P04TGI7F3;P030Y7Y11;PE35RV747;Q2UKLVVX4
2  ODJSMDK   Q2UKLVVX4;PWER00711;PE35RV747;Q2UKLVVX4
3 JDKSAKDJ                       PE35RV747;P0F071G1G

我想匹配用“;”分隔的字符串使用以下数据框中的值:

      Var_x    Var_y
1 P04TGI7F3     good
2 P030Y7Y11   normal
3 PE35RV747      bad
4 Q2UKLVVX4   normal

所以生成的数据框看起来像:

      Var1                                      Var2                    Var3
1   SJDJWK   P04TGI7F3;P030Y7Y11;PE35RV747;Q2UKLVVX4  good;normal;bad;normal
2  ODJSMDK   Q2UKLVVX4;PWER00711;PE35RV747;Q2UKLVVX4       normal;bad;normal
3 JDKSAKDJ                       PE35RV747;P0F071G1G                     bad

到目前为止,我尝试使用模糊连接来做到这一点:

fuzzy_left_join(Data1, Data2, by = c("Var2"="Var_x"), match_fun = str_detect)

这可以完成工作,但它使用大量内存(我的数据集非常大,R 停止工作)。我试图用 for 循环来做到这一点,但我不知道该怎么做。有谁知道吗?

【问题讨论】:

  • 一个选项将宽改成长格式,然后使用普通连接,然后再次将长改成宽。

标签: r string join match


【解决方案1】:

这是来自tidyverse 的一个想法。我们将行分开,在第二个数据帧上合并,然后根据Var1 再次连接,

library(tidyverse)

df1 %>% 
 separate_rows(Var2) %>% 
 left_join(df2, by = c('Var2' = 'Var_x')) %>% 
 group_by(Var1) %>% 
 summarise_all(funs(paste(., collapse = ';')))

给出,

# A tibble: 3 x 3
  Var1     Var2                                    Var_y                 
  <fct>    <chr>                                   <chr>                 
1 JDKSAKDJ PE35RV747;P0F071G1G                     bad;NA                
2 ODJSMDK  Q2UKLVVX4;PWER00711;PE35RV747;Q2UKLVVX4 normal;NA;bad;normal  
3 SJDJWK   P04TGI7F3;P030Y7Y11;PE35RV747;Q2UKLVVX4 good;normal;bad;normal

如果您不想包含NAs,我们可以在加入之前省略(如@akrun 所述),即

df1 %>% 
 separate_rows(Var2) %>% 
 filter(Var2 %in% df2$Var_x) %>% 
 left_join(df2, by = c('Var2' = 'Var_x')) %>% 
 group_by(Var1) %>% 
 summarise_all(funs(paste(., collapse = ';')))

给出,

# A tibble: 3 x 3
  Var1     Var2                                    Var_y                 
  <fct>    <chr>                                   <chr>                 
1 JDKSAKDJ PE35RV747                               bad                   
2 ODJSMDK  Q2UKLVVX4;PE35RV747;Q2UKLVVX4           normal;bad;normal     
3 SJDJWK   P04TGI7F3;P030Y7Y11;PE35RV747;Q2UKLVVX4 good;normal;bad;normal

【讨论】:

  • 也许您需要在left_join 之前使用filter 来避免NA 即%&gt;% filter(Var2 %in% df2$Var_x)
  • @akrun 我想过但我把它们留在那里也代表不匹配的字符串。让我添加它以完成
  • 非常感谢@Sotos
【解决方案2】:

data.table

df2[df1[, unlist(tstrsplit(Var2, ";")), Var1], 
    on = .(Var_x = V1)
    ][,
      lapply(.SD, function(x) paste(x[!is.na(x)], collapse = ";")), 
      by = Var1
      ]

       Var1                                   Var_x                  Var_y
1:   SJDJWK P04TGI7F3;P030Y7Y11;PE35RV747;Q2UKLVVX4 good;normal;bad;normal
2:  ODJSMDK Q2UKLVVX4;PWER00711;PE35RV747;Q2UKLVVX4      normal;bad;normal
3: JDKSAKDJ                     PE35RV747;P0F071G1G                    bad

【讨论】:

  • 第二列发生了什么?
  • @zx8754 对不起...?
  • 将您的第二列与 OP 的预期输出进行比较。
【解决方案3】:

另一个使用gsubfn 的选项,我们在gsubfnreplacement 中为不是; 的字符的pattern 创建键/值对的list。请注意,在某些情况下,单词中不存在 'df2'。如果所有替换都在那里,则不需要第二个gsub(假设在示例中未提供)

library(gsubfn)
df1$Var_y <-  gsub(";[A-Z0-9]+", "", gsubfn("[^;]+",  
                setNames(as.list(df2$Var_y), df2$Var_x), df1$Var2))
df1
#      Var1                                    Var2                  Var_y
#1   SJDJWK P04TGI7F3;P030Y7Y11;PE35RV747;Q2UKLVVX4 good;normal;bad;normal
#2  ODJSMDK Q2UKLVVX4;PWER00711;PE35RV747;Q2UKLVVX4      normal;bad;normal
#3 JDKSAKDJ                     PE35RV747;P0F071G1G                    bad

数据

df1 <- structure(list(Var1 = c("SJDJWK", "ODJSMDK", "JDKSAKDJ"), 
  Var2 = c("P04TGI7F3;P030Y7Y11;PE35RV747;Q2UKLVVX4", 
"Q2UKLVVX4;PWER00711;PE35RV747;Q2UKLVVX4", "PE35RV747;P0F071G1G"
)), class = "data.frame", row.names = c("1", "2", "3"))

df2 <- structure(list(Var_x = c("P04TGI7F3", "P030Y7Y11", "PE35RV747", 
"Q2UKLVVX4"), Var_y = c("good", "normal", "bad", "normal")), 
     class = "data.frame", row.names = c("1", 
"2", "3", "4"))

【讨论】:

    【解决方案4】:

    使用base R,遍历Var2,在";"上分割,然后匹配

    df1$Var3 <- sapply(df1$Var2, function(i){
      paste(df2$Var_y[ match(unlist(strsplit(i, split = ";")), df2$Var_x) ], collapse = ";")
    })
    
    #       Var1                                    Var2                   Var3
    # 1   SJDJWK P04TGI7F3;P030Y7Y11;PE35RV747;Q2UKLVVX4 good;normal;bad;normal
    # 2  ODJSMDK Q2UKLVVX4;PWER00711;PE35RV747;Q2UKLVVX4   normal;NA;bad;normal
    # 3 JDKSAKDJ                     PE35RV747;P0F071G1G                 bad;NA
    

    注意:我知道我的输出在 Var3 中有 NA,而 OP 的预期输出没有。但我更喜欢保留 NA,以便我们可以按原始字符串中的位置将每个“坏/正常”匹配到相应的字符串。当然,如果 OP 愿意,可以使用na.omit 删除它们:

    df1$Var3 <- sapply(df1$Var2, function(i){
      paste(na.omit(df2$Var_y[ match(unlist(strsplit(i, split = ";")), df2$Var_x) ]), collapse = ";")
    })
    #       Var1                                    Var2                   Var3
    # 1   SJDJWK P04TGI7F3;P030Y7Y11;PE35RV747;Q2UKLVVX4 good;normal;bad;normal
    # 2  ODJSMDK Q2UKLVVX4;PWER00711;PE35RV747;Q2UKLVVX4      normal;bad;normal
    # 3 JDKSAKDJ                     PE35RV747;P0F071G1G                    bad
    

    【讨论】:

    • Var3 发生了什么,预期输出中没有 NA :)
    • @snoram haha​​,知道有人会提到这个。我会保留 NA,以便我们可以将每个错误/正常与原始字符串位置匹配。
    【解决方案5】:

    在基础R 中,您可以从第二个data.frame 构建一个命名向量,然后在拆分后我们在这个命名向量中查找所有相关值,删除 NA 并重新粘贴在一起。

    df1$Var3 <- sapply(
      strsplit(df1$Var2,";"), 
      function(x) paste(na.omit(setNames(df2$Var_y,df2$Var_x)[x]), collapse=";"))
    
    df1
    #       Var1                                    Var2                   Var3
    # 1   SJDJWK P04TGI7F3;P030Y7Y11;PE35RV747;Q2UKLVVX4 good;normal;bad;normal
    # 2  ODJSMDK Q2UKLVVX4;PWER00711;PE35RV747;Q2UKLVVX4      normal;bad;normal
    # 3 JDKSAKDJ                     PE35RV747;P0F071G1G                    bad
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-11-03
      • 2017-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-26
      • 2015-06-18
      • 1970-01-01
      相关资源
      最近更新 更多