【问题标题】:Identify records in data frame A not contained in data frame B [closed]识别数据框A中不包含在数据框B中的记录[关闭]
【发布时间】:2011-12-05 10:06:04
【问题描述】:

这是我第一次在这里发帖,请多多关照;-)

编辑 在我有机会做出向我建议的更改之前,我的问题已经结束。所以我现在正在努力做得更好,感谢到目前为止所有回答的人!

问题

如何根据可用的所有属性(即所有列)识别数据框x.1包含在数据框x.2中的记录/行以最有效的方式?

示例数据

> x.1 <- data.frame(a=c(1,2,3,4,5), b=c(1,2,3,4,5))
> x.1
  a b
1 1 1
2 2 2
3 3 3
4 4 4
5 5 5

> x.2 <- data.frame(a=c(1,1,2,3,4), b=c(1,1,99,3,4))
> x.2
  a  b
1 1  1
2 1  1
3 2 99
4 3  3
5 4  4

期望的结果

  a b
2 2 2
5 5 5

迄今为止最好的解决方案

Brian Ripley 教授和 Gabor Grothendieck 教授

> fun.12 <- function(x.1,x.2,...){
+     x.1p <- do.call("paste", x.1)
+     x.2p <- do.call("paste", x.2)
+     x.1[! x.1p %in% x.2p, ]
+ }
> fun.12(x.1,x.2)
  a b
2 2 2
5 5 5
> sol.12 <- microbenchmark(fun.12(x.1,x.2))
> sol.12 <- median(sol.12$time)/1000000000
> sol.12
> [1] 0.000207784

我的blog提供了迄今为止测试过的所有解决方案的集合@

最终编辑 2011-10-14

这是封装在函数“mergeX()”中的最佳解决方案:

setGeneric(
    name="mergeX",
    signature=c("src.1", "src.2"),
    def=function(
        src.1,
        src.2,
        ...
    ){
    standardGeneric("mergeX")    
    }
)

setMethod(
    f="mergeX", 
    signature=signature(src.1="data.frame", src.2="data.frame"), 
    definition=function(
        src.1,
        src.2,
        do.inverse=FALSE,
        ...
    ){
    if(!do.inverse){
        out <- merge(x=src.1, y=src.2, ...)
    } else {
        if("by.y" %in% names(list(...))){
            src.2.0 <- src.2
            src.2 <- src.1
            src.1 <- src.2.0
        }
        src.1p <- do.call("paste", src.1)
        src.2p <- do.call("paste", src.2)
        out <- src.1[! src.1p %in% src.2p, ]
    }
    return(out)    
    }
)

【问题讨论】:

  • 这不是一个真正的问题。您针对自己的问题发布了 8 种不同的解决方案,其中最好的(由 Brian Ripley 教授撰写)只有三行 Base R 代码,并且已经打包成一个函数。你还想要什么?
  • @Andrie,他可能想要我的解决方案? :-))
  • @Andrie:我害怕发生这样的事情 ;-) 仍然认为我确实提出了一个问题,为什么不能将问题与可能的解决方案的集合联系起来,而不是解决方案分散在各处这个地方,因为每个人都用不同的措辞来描述他/她所追求的?只是想帮助别人一点...
  • 我认为结束这个问题是一个非常不具建设性和愚蠢的举动。给他机会改进这个问题。这样你就强迫他开始一个新的。这样更好吗?
  • 这个问题仍然需要投票才能重新开放。我建议编辑问题(添加一些解决方案)并清楚说明您的问题是什么,然后标记它。

标签: r join merge match dataframe


【解决方案1】:

这里有几种方法。 #1 和 #4 假设 x.1 的行是唯一的。 (如果x.1 的行不是唯一的,那么它们将只返回重复行中的一个重复项。)其他返回所有重复项:

# 1
x.1[!duplicated(rbind(x.2, x.1))[-(1:nrow(x.2))],]

# 2
do.call("rbind", setdiff(split(x.1, rownames(x.1)), split(x.2, rownames(x.2))))

# 3
x.1p <- do.call("paste", x.1)
x.2p <- do.call("paste", x.2)
x.1[! x.1p %in% x.2p, ]

# 4
library(sqldf)
sqldf("select * from `x.1` except select * from `x.2`")

编辑:x.1 和 x.2 已交换并且已修复。还更正了开头的限制说明。

【讨论】:

  • 谢谢加博!并非所有方法都返回所需的结果((2,2)和(5,5)),但给了我一些学习的东西。特别是关于'sqldf'。以前从没看过,但看起来真的很酷,谢谢!
  • 在某些解决方案中,x.1 和 x.2 的角色部分颠倒了。我已经对其进行了编辑,现在它们都应该给出 (2,2) 和 (5,5)。
  • @Gabor:非常感谢编辑它
  • 我更新了我的基准测试结果,您的第三种方法似乎是最快的(0.0002066945 秒)!再次感谢!
【解决方案2】:

如何使用merge - 最简单的解决方案 - 我认为它也是最快的。

tmp = merge(x.1, cbind(x.2, myid = 1:nrow(x.2)), all.x = TRUE)
    # provided that there's no column myid in both dataframes
tmp[is.na(tmp$myid), 1:ncol(x.1)] # the result

对应于:

select x1.* 
from x1 natural left join x2 
where x2.myid is NULL

(您也可以使用 sqldf 来执行此操作)。

请注意,添加列 myid 是为了确保有一些列没有 NA 值。如果您确定已经有一些列不包含 NULL 值,您可以简化解决方案:

tmp = merge(x.1, x.2, all.x = TRUE)
    # provided that there's no column myid in both dataframes
tmp[is.na(tmp$some_column), 1:ncol(x.1)] # the result

【讨论】:

  • 简洁的解决方案,但声称是最快的 - 如果您能证明您的主张是正确的,那么值得一票。
  • 感谢托马斯的回答!那是我一直在寻找的,但它不是最快的 ;-)
  • 与我上面收集的相比排名第7(使用微基准测试0.002583893秒)
  • @Andrie,我懒得这样做,我让测试给了songpants......但是你可能是对的,它不是那么明显:在SQL中它会是最快的,因为它是针对连接进行了优化,使用 R 的合并我现在不太确定...
  • @songpants,我们互相交谈 :-) 感谢您的测量,是的,我对 R 合并的担忧成真了……谢谢。
猜你喜欢
  • 2013-06-03
  • 2015-01-21
  • 1970-01-01
  • 1970-01-01
  • 2023-01-07
  • 2017-04-15
  • 2019-09-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多