【问题标题】:R: merge two data frames when either of two criteria matchesR:当两个条件中的任何一个匹配时合并两个数据帧
【发布时间】:2016-08-03 20:32:00
【问题描述】:

假设我有两个如下数据框:

n = c(2, 3, 5, 5, 6, 7) 
s = c("aa", "bb", "cc", "dd", "ee", "ff") 
b = c(2, 4, 5, 4, 3, 2) 
df = data.frame(n, s, b)
#  n  s b
#1 2 aa 2
#2 3 bb 4
#3 5 cc 5  
#4 5 dd 4
#5 6 ee 3
#6 7 ff 2

n2 = c(5, 6, 7, 6) 
s2 = c("aa", "bb", "cc", "ll") 
b2 = c("hh", "nn", "ff", "dd")  
df2 = data.frame(n2, s2, b2)

 #   n2 s2 b2
 #1  5 aa hh
 #2  6 bb nn
 #3  7 cc ff
 #4  6 ll dd

我想将它们合并以达到以下结果:

 #n s  b n2 s2 b2
 #2 aa 2 5  aa hh
 #3 bb 4 6  bb nn
 #5 cc 5 7  cc ff
 #5 dd 4 6  ll dd

基本上,我想要实现的是在 data2 的 s2 或 b2 列中找到第一个数据的 s 中的值时合并两个数据帧。

我知道当我从每个数据帧中指定两列时,合并可以工作,但我不确定如何在合并函数中添加 OR 条件。或者如何使用 dpylr 等软件包中的其他命令来实现这一目标。

另外,澄清一下,会出现 s2 和 b2 与同一行中的 s 列匹配的情况。如果是这种情况,那么只需将它们合并一次。

【问题讨论】:

    标签: r merge data-manipulation


    【解决方案1】:

    如果你熟悉 SQL,你可以使用它:

    library(sqldf)
    res <- sqldf("SELECT l.*, r.*
                  FROM df as l
                  INNER JOIN df2 as r
                  on l.s = r.s2 OR l.s = r.b2")
    
    res
      n  s b n2 s2 b2
    1 2 aa 2  5 aa hh
    2 3 bb 4  6 bb nn
    3 5 cc 5  7 cc ff
    4 5 dd 4  6 ll dd
    5 7 ff 2  7 cc ff
    

    数据

    df<-structure(list(n = c(2, 3, 5, 5, 6, 7), s = structure(1:6, .Label = c("aa", 
    "bb", "cc", "dd", "ee", "ff"), class = "factor"), b = c(2, 4, 
    5, 4, 3, 2)), .Names = c("n", "s", "b"), row.names = c(NA, -6L
    ), class = "data.frame")
    
    df2<-structure(list(n2 = c(5, 6, 7, 6), s2 = structure(1:4, .Label = c("aa", 
    "bb", "cc", "ll"), class = "factor"), b2 = structure(c(3L, 4L, 
    2L, 1L), .Label = c("dd", "ff", "hh", "nn"), class = "factor")), .Names = c("n2", 
    "s2", "b2"), row.names = c(NA, -4L), class = "data.frame")
    

    【讨论】:

    • 我试过了,但它在语句中说错误:没有这样的表:df2,而df2存储在工作环境中
    • 我重新运行,它对我有用。我也发布了我使用的数据(应该和你的一样)。
    • 它仍然不能在我自己的数据上运行,而它适用于我发布的示例数据(我也只是运行了这个)。有没有其他方法可以将两者合并?
    • 与普通合并相比,这个速度有多快?
    【解决方案2】:

    几个问题:1)您已经构建了几个数据帧,其中的因素往往会破坏匹配和索引,所以我在 hte 数据帧调用中使用了 stringsAsFactors =FALSE。 2)当 s2 和 b2 在 s 列中都有匹配项时(如您的示例中确实发生的那样),您有一个模棱两可的情况,没有明确的解决方案:

    > df2[c("s")] <- list( c( df$s[pmax( match( df2$s2 , df$s), match(df2$b2, df$s),na.rm=TRUE)]))
    > df2
      n2 s2 b2  s
    1  5 aa hh aa
    2  6 bb nn bb
    3  7 cc ff ff
    4  6 ll dd dd
    > df2[c("s")] <- list( c( df$s[pmin( match( df2$s2 , df$s), match(df2$b2, df$s),na.rm=TRUE)]))
    > df2
      n2 s2 b2  s
    1  5 aa hh aa
    2  6 bb nn bb
    3  7 cc ff cc
    4  6 ll dd dd
    

    一旦您解决了对您的满意的歧义,只需使用相同的方法来提取和匹配“b”:

    > df2[c("b")] <- list( c( df$b[pmin( match( df2$s2 , df$s), match(df2$b2, df$s),na.rm=TRUE)]))
    > df2
      n2 s2 b2  s b
    1  5 aa hh aa 2
    2  6 bb nn bb 4
    3  7 cc ff cc 5
    4  6 ll dd dd 4
    

    修改后的df:

    > dput(df)
    structure(list(n = c(2, 3, 5, 5, 6, 7), s = c("aa", "bb", "cc", 
    "dd", "ee", "ff"), b = c(2, 4, 5, 4, 3, 2)), .Names = c("n", 
    "s", "b"), row.names = c(NA, -6L), class = "data.frame")
    > dput(df2)
    structure(list(n2 = c(5, 6, 7, 6), s2 = c("aa", "bb", "cc", "ll"
    ), b2 = c("hh", "nn", "ff", "dd"), s = c("aa", "bb", "cc", "dd"
    ), b = c(2, 4, 5, 4)), row.names = c(NA, -4L), .Names = c("n2", 
    "s2", "b2", "s", "b"), class = "data.frame")
    

    一步解决:

    > df2[c("s", "c")] <-  df[pmin( match( df2$s2 , df$s), match(df2$b2, df$s),na.rm=TRUE), c("s", "b")]
    > df2
      n2 s2 b2  s c
    1  5 aa hh aa 2
    2  6 bb nn bb 4
    3  7 cc ff cc 5
    4  6 ll dd dd 4
    

    【讨论】:

    • 它可以工作,但我实际拥有的两个数据框都超过 30 列。如果我一次添加一列,将花费太长时间。另外,澄清一下,不会出现 s2 和 b2 与同一行中的 s 列匹配的情况。
    • 我将发布一个紧凑的,它可以一步完成两列:
    • 感谢您修改解决方案。但我的意思是,在实际数据中,每个数据总共有 30 多列。 2列是第一个数据框中的“s”和“c”。我想使用这些列作为选择标准,而不会丢失我没有发布的其他列(其他 20 多列)。是不是在这个解决方案中,它实际上是向 df2 添加了两列,但我真正想要的是除了这两列之外,还有 20 多列的结果。
    • 您的评论并没有用语言解释您的困难,让我了解问题所在。特别是我不明白为什么您不能通过将它们的名称添加到上述版本中“[”的j-argument 来从第一个df 中“拖动”任何其他列。
    • 你的意思是如果我想将三列s,c,a添加到df2,它将是df2[c(“s”,“c”,“a”)]?
    【解决方案3】:

    我们可以使用模糊连接,如果您有大数据,在这种情况下它可能不是很有效,但它肯定是可读的。使用我的包safejoin 包裹(在这种情况下) fuzzyjoin

    # devtools::install_github("moodymudskipper/safejoin")
    library(safejoin)
    safe_inner_join(df, df2, ~ X("s") == Y("s2") | X("s") == Y("b2"))
    #   n  s b n2 s2 b2
    # 1 2 aa 2  5 aa hh
    # 2 3 bb 4  6 bb nn
    # 3 5 cc 5  7 cc ff
    # 4 5 dd 4  6 ll dd
    # 5 7 ff 2  7 cc ff
    

    fuzzyjoin 语法是:

    library(fuzzyjoin)
    fuzzy_inner_join(df, df2, match_fun = NULL, 
                     multi_by = list(x = "s", y= c("s2","b2")), 
                     multi_match_fun = function(x,y) x == y[,"s2"] | x == y[,"b2"])
    #   n  s b n2 s2 b2
    # 1 2 aa 2  5 aa hh
    # 2 3 bb 4  6 bb nn
    # 3 5 cc 5  7 cc ff
    # 4 5 dd 4  6 ll dd
    # 5 7 ff 2  7 cc ff
    

    【讨论】:

      【解决方案4】:

      一种基本方法是绑定两个合并。您需要在df2 中重新创建相应的连接键以有效连接帧。此外,#5 行没有出现在预期的结果中:

      t1 <- merge(df, df2, by.x=c("s"), by.y=c("s2"))
      t1$s2 <- t1$s
      
      t2 <- merge(df, df2, by.x=c("s"), by.y=c("b2"))
      t2$b2 <- t2$s
      
      finaldf <- rbind(t1, t2)
      
      #    s n b n2 b2 s2
      # 1 aa 2 2  5 hh aa
      # 2 bb 3 4  6 nn bb
      # 3 cc 5 5  7 ff cc
      # 4 dd 5 4  6 dd ll
      # 5 ff 7 2  7 ff cc
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-04-14
        • 2021-12-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多