Подтвердить что ты не робот

Идентификация записей в кадре данных A, не содержащихся в кадре данных B

Это моя первая публикация, поэтому, пожалуйста, будьте добры, -)

ИЗМЕНИТЬ Мой вопрос был закрыт, прежде чем у меня появилась возможность внести изменения, предложенные мне. Поэтому я стараюсь сделать лучшую работу сейчас, спасибо всем, кто ответил до сих пор!

Вопрос

Как я могу идентифицировать записи/строки в кадре данных 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

ЛУЧШЕЕ РЕШЕНИЕ SO FAR

Проф. Брайан Рипли и Габор Гротендик

> 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

Коллекция всех проверенных до сих пор решений доступна в моем блоге

ОКОНЧАТЕЛЬНЫЙ РЕДАКТИРОВАНИЕ 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)    
    }
)
4b9b3361

Ответ 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`")

EDIT: x.1 и x.2 были заменены, и это было исправлено. Также исправили примечание об ограничениях в начале.

Ответ 2

Как насчет использования merge - самого простого возможного решения - я думаю, что это также самый быстрый.

tmp = merge(x.1, cbind(x.2, myid = 1:nrow(x.2)), all.x = TRUE)
    # provided that there 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 no column myid in both dataframes
tmp[is.na(tmp$some_column), 1:ncol(x.1)] # the result