【问题标题】:Find complement of a data frame (anti - join)查找数据帧的补码(反连接)
【发布时间】:2021-12-25 19:21:21
【问题描述】:

我有两个数据框(df 和 df1)。 df1 是 df 的子集。我想得到一个数据框,它是 df 中 df1 的补码,即返回第一个数据集中在第二个数据集中不匹配的行。比如让,

数据框df:

heads
row1
row2
row3
row4
row5

数据框df1:

heads
row3
row5

那么想要的输出df2是:

heads
row1
row2
row4

【问题讨论】:

    标签: r join merge r-faq


    【解决方案1】:

    迟到的答案,但对于另一种选择,我们可以尝试使用sqldf 包进行正式的 SQL 反连接:

    library(sqldf)
    sql <- "SELECT t1.heads
            FROM df t1 LEFT JOIN df1 t2
                ON t1.heads = t2.heads
            WHERE t2.heads IS NULL"
    df2 <- sqldf(sql)
    

    sqldf 包对于使用 SQL 逻辑很容易表达但使用基本 R 或其他 R 包可能不太容易表达的问题很有用。

    【讨论】:

      【解决方案2】:

      您还可以使用data.tables 二进制连接进行某种类型的反连接

      library(data.table)
      setkey(setDT(df), heads)[!df1]
      #    heads
      # 1:  row1
      # 2:  row2
      # 3:  row4
      

      编辑:开始 data.table v1.9.6+ 我们可以在使用on时加入 data.tables 而无需设置键

      setDT(df)[!df1, on = "heads"]
      

      EDIT2: 开始 data.table v1.9.8+ fsetdiff 被引入,它基本上是上述解决方案的变体,只是在所有列名之上xdata.table,例如x[!y, on = names(x)]。如果all 设置为FALSE(默认行为),则只会返回x 中的唯一行。对于每个data.table中只有一列的情况,以下将等效于以前的解决方案

      fsetdiff(df, df1, all = TRUE)
      

      【讨论】:

      • 我喜欢设置键后使用merge[df,df2]。这个操作的逆运算是什么?
      • @HermanToothrot 答案实际上是df[!df2],这与您所写的相反
      • 如果on中的键名不同怎么办?
      • @JdeMello 您可以指定不同的键 - 请参阅 ?data.table 在“参数”部分查找 on
      • AFAIK fsetdiff(all=T) 无济于事,它将整行视为元素并减去重复行的数量。 X=1,1,1,1; Y=1,1;然后它给出两行,因为 4-2=2。 Antijoin 将给出 0。
      【解决方案3】:

      dplyr 也有 setdiff() 可以为您提供

      setdiff(bigFrame, smallFrame) 为您获取第一个表中的额外记录。

      所以对于 OP 的示例,代码将显示为 setdiff(df, df1)

      dplyr 有很多很棒的功能:有关快速简便的指南,请参阅here.

      【讨论】:

      【解决方案4】:

      通过操作plyr 包的match_df 的代码来创建函数negate_match_df 的另一种选择。

      library(plyr)
      negate_match_df <- function (x, y, on = NULL) 
      {
      if (is.null(on)) {
          on <- intersect(names(x), names(y))
          message("Matching on: ", paste(on, collapse = ", "))
      }
      keys <- join.keys(x, y, on)
      x[!keys$x %in% keys$y, , drop = FALSE]
      }
      

      数据

      df <- read.table(text ="heads
      row1
      row2
      row3
      row4
      row5",header=TRUE)
      
      df1 <- read.table(text ="heads
      row3
      row5",header=TRUE)
      

      输出

      negate_match_df(df,df1)
      

      【讨论】:

        【解决方案5】:

        另一种选择,使用基础 R 和 setdiff 函数:

        df2 <- data.frame(heads = setdiff(df$heads, df1$heads))
        

        setdiff 的功能与您想象的完全一样;将两个参数作为集合,并从第一个中删除第二个中的所有项目。

        我发现 setdiff 更易读 tahtn %in% 并且不想在我不需要时不需要额外的库,但你使用哪个答案很大程度上是个人品味的问题。

        【讨论】:

        • setdiff%in% 的功能几乎相同。
        • @DavidArenburg- 同意,这就是为什么我认为这只是易读性和品味的问题。
        • @DavidArenburg,那个函数是match
        • @MaratTalipov 我知道,这就是我的暗示
        【解决方案6】:

        尝试%in% 命令并使用! 反转它

        df[!df$heads %in% df1$heads,]
        

        【讨论】:

        • 支持基本 R 解决方案,尽管它可能不是这里最快的解决方案。
        • 如果我想进行反连接并有多个键/列,语法是什么样的?
        • @Blind0ne:您可以使用interactionpaste 反连接多个键。 key &lt;- c("colName1", "colName2"); df[!interaction(df[key]) %in% interaction(df1[key]),]
        【解决方案7】:

        dplyr 尝试anti_join

        library(dplyr)
        anti_join(df, df1, by='heads')
        

        【讨论】:

          猜你喜欢
          • 2016-12-18
          • 2019-01-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-02-02
          • 2021-11-23
          相关资源
          最近更新 更多