data.frame 2에 존재하지 않는 data.frame 1의 행을 찾으려면 두 개의 data.frame을 비교하십시오.


161

다음과 같은 2 개의 data.frame이 있습니다.

a1 <- data.frame(a = 1:5, b=letters[1:5])
a2 <- data.frame(a = 1:3, b=letters[1:3])

a1에 a2가없는 행을 찾고 싶습니다.

이 유형의 작업에 내장 된 기능이 있습니까?

(ps : 해결책을 작성했습니다. 누군가 이미 더 만들어진 코드를 만들면 궁금합니다.)

내 해결책은 다음과 같습니다.

a1 <- data.frame(a = 1:5, b=letters[1:5])
a2 <- data.frame(a = 1:3, b=letters[1:3])

rows.in.a1.that.are.not.in.a2  <- function(a1,a2)
{
    a1.vec <- apply(a1, 1, paste, collapse = "")
    a2.vec <- apply(a2, 1, paste, collapse = "")
    a1.without.a2.rows <- a1[!a1.vec %in% a2.vec,]
    return(a1.without.a2.rows)
}
rows.in.a1.that.are.not.in.a2(a1,a2)

답변:


88

이것은 귀하의 질문에 직접 대답하지는 않지만 공통적 인 요소를 제공합니다. 이것은 Paul Murrell의 패키지를 사용하여 수행 할 수 있습니다 compare.

library(compare)
a1 <- data.frame(a = 1:5, b = letters[1:5])
a2 <- data.frame(a = 1:3, b = letters[1:3])
comparison <- compare(a1,a2,allowAll=TRUE)
comparison$tM
#  a b
#1 1 a
#2 2 b
#3 3 c

이 함수 compare는 어떤 종류의 비교가 허용되는지 (예 : 각 벡터의 요소 순서 변경, 변수 순서 및 이름 변경, 변수 단축, 문자열 대 / 소문자 변경) 측면에서 많은 유연성을 제공합니다. 이것에서, 당신은 어느 하나에서 누락 된 것을 알아낼 수 있어야합니다. 예를 들어 (매우 우아하지는 않습니다) :

difference <-
   data.frame(lapply(1:ncol(a1),function(i)setdiff(a1[,i],comparison$tM[,i])))
colnames(difference) <- colnames(a1)
difference
#  a b
#1 4 d
#2 5 e

3
이 기능이 혼란 스럽다는 것을 알았습니다. 나는 그것이 나를 위해 작동 할 것이라고 생각했지만 한 세트에 다른 세트의 동일하게 일치하는 행이 포함되어 있으면 위와 같이 작동하는 것 같습니다. 이 경우를 고려하십시오 a2 <- data.frame(a = c(1:3, 1), b = c(letters[1:3], "c")). 그대로 두십시오 a1. 이제 비교를 시도하십시오. 일반적인 요소 만 나열하는 올바른 방법이 무엇인지 옵션을 읽을 때조차 분명하지 않습니다.
Hendy

148

SQLDF 좋은 해결책을 제공합니다

a1 <- data.frame(a = 1:5, b=letters[1:5])
a2 <- data.frame(a = 1:3, b=letters[1:3])

require(sqldf)

a1NotIna2 <- sqldf('SELECT * FROM a1 EXCEPT SELECT * FROM a2')

그리고 두 데이터 프레임에있는 행 :

a1Ina2 <- sqldf('SELECT * FROM a1 INTERSECT SELECT * FROM a2')

의 새 버전 dplyr에는 anti_join정확히 이러한 종류의 비교를위한 기능 이 있습니다.

require(dplyr) 
anti_join(a1,a2)

그리고 그 semi_join안에 a1있는 행을 필터링하기 위해a2

semi_join(a1,a2)

18
에 대한 감사 anti_joinsemi_join!
drastega

sqldf와 같이 anti_join이 null DF를 반환하는 이유가 있지만 same (a1, a2) 및 all.equal () 함수가 그와 상충되는 이유는 무엇입니까?
3pitt

anti_join과 semi_join이 내 경우와 같은 경우에는 작동하지 않는다는 것을 여기에 추가하고 싶었습니다. 데이터 프레임에 대해 "오류 : 열은 1d 원자 벡터 또는 목록이어야합니다"라는 메시지가 표시됩니다. 이러한 기능이 작동하도록 데이터를 처리 할 수 ​​있습니다. Sqldf는 바로 게이트에서 작동했습니다!
Akshay Gaur

@AkshayGaur 데이터 형식이거나 데이터 정리 문제 일뿐입니다. sqldf는 단지 sql입니다. 모든 것이 nromal DB와 같이 사전 처리되어 데이터에서 sql을 실행할 수 있습니다.
stucash

75

에서 dplyr :

setdiff(a1,a2)

기본적으로 setdiff(bigFrame, smallFrame)첫 번째 테이블에서 추가 레코드를 얻습니다.

SQLverse에서는 이것을

결합 벤 다이어그램을 제외한 왼쪽

모든 조인 옵션 및 주제 설정에 대한 자세한 설명을 위해 지금까지 내가 본 최고의 요약 중 하나입니다. http://www.vertabelo.com/blog/technical-articles/sql-joins

그러나이 질문으로 돌아 가면 setdiff()OP의 데이터를 사용할 때 의 코드 결과는 다음과 같습니다.

> a1
  a b
1 1 a
2 2 b
3 3 c
4 4 d
5 5 e

> a2
  a b
1 1 a
2 2 b
3 3 c

> setdiff(a1,a2)
  a b
1 4 d
2 5 e

또는 anti_join(a1,a2)동일한 결과를 얻을 수도 있습니다.
자세한 정보 : https://www.rstudio.com/wp-content/uploads/2015/02/data-wrangling-cheatsheet.pdf


2
영업 이익 항목을 요청 이후에 a1해당하지에 a2당신은 같은 사용 뭔가 싶지 않아 semi_join(a1, a2, by = c('a','b'))? "Rickard"의 답변에서 나는 그것이 semi_join제안 된 것을 본다 .
steveb

확실한! 또 다른 훌륭한 선택이기도합니다. 특히 조인 키만 있고 열 이름이 다른 데이터 프레임이있는 경우에 특히 그렇습니다.
leerssej

setdiff는 lubridate :: setdiff에서 왔으며 library (dplyr)가 아닙니다
mtelesha

@mtelesha-흠, dplyr에 대한 문서 및 소스 코드 는 거기 에 있음을 보여줍니다 ( dplyr.tidyverse.org/reference/setops.html , github.com/tidyverse/dplyr/blob/master/R/sets. ). 또한 dplyr 라이브러리가로드되면 setdiff()두 벡터에서 작동 하는 기본 기능인 stat.ethz.ch/R-manual/R-devel/library/base/html/sets.html 마스킹도보고 됩니다. 어쩌면 당신은로드 한 lubridate의 후 라이브러리 dplyr을 그리고 그것은 tabcomplete 목록에서 소스로 제안한다?
leerssej

1
lubridate와 dplyr 사이에 충돌이 참조 github.com/tidyverse/lubridate/issues/693
slhck

39

이 특정 목적에는 확실히 효과적이지 않지만 이러한 상황에서 자주하는 일은 각 data.frame에 지표 변수를 삽입 한 다음 병합하는 것입니다.

a1$included_a1 <- TRUE
a2$included_a2 <- TRUE
res <- merge(a1, a2, all=TRUE)

included_a1에서 누락 된 값은 a1에서 누락 된 행을 나타냅니다. a2와 유사합니다.

솔루션의 한 가지 문제점은 열 순서가 일치해야한다는 것입니다. 또 다른 문제는 실제로 행이 다르면 행이 동일하게 코딩되는 상황을 쉽게 상상할 수 있다는 것입니다. 병합을 사용하면 좋은 솔루션에 필요한 모든 오류 검사를 무료로 얻을 수 있다는 이점이 있습니다.


따라서 ... 결 측값을 찾을 때 다른 결 측값을 작성합니다. 결 측값은 어떻게 찾 included_a1습니까? :-/
Louis Maddox

1
is.na () 및 일부 또는 dplyr :: 필터 사용
실바 레오니

새 라이브러리를 설치하지 않고 방법을 가르쳐 주셔서 감사합니다!
Rodrigo

27

나는 같은 문제가 있었기 때문에 패키지 ( https://github.com/alexsanjoseph/compareDF )를 썼습니다 .

  > df1 <- data.frame(a = 1:5, b=letters[1:5], row = 1:5)
  > df2 <- data.frame(a = 1:3, b=letters[1:3], row = 1:3)
  > df_compare = compare_df(df1, df2, "row")

  > df_compare$comparison_df
    row chng_type a b
  1   4         + 4 d
  2   5         + 5 e

더 복잡한 예 :

library(compareDF)
df1 = data.frame(id1 = c("Mazda RX4", "Mazda RX4 Wag", "Datsun 710",
                         "Hornet 4 Drive", "Duster 360", "Merc 240D"),
                 id2 = c("Maz", "Maz", "Dat", "Hor", "Dus", "Mer"),
                 hp = c(110, 110, 181, 110, 245, 62),
                 cyl = c(6, 6, 4, 6, 8, 4),
                 qsec = c(16.46, 17.02, 33.00, 19.44, 15.84, 20.00))

df2 = data.frame(id1 = c("Mazda RX4", "Mazda RX4 Wag", "Datsun 710",
                         "Hornet 4 Drive", " Hornet Sportabout", "Valiant"),
                 id2 = c("Maz", "Maz", "Dat", "Hor", "Dus", "Val"),
                 hp = c(110, 110, 93, 110, 175, 105),
                 cyl = c(6, 6, 4, 6, 8, 6),
                 qsec = c(16.46, 17.02, 18.61, 19.44, 17.02, 20.22))

> df_compare$comparison_df
    grp chng_type                id1 id2  hp cyl  qsec
  1   1         -  Hornet Sportabout Dus 175   8 17.02
  2   2         +         Datsun 710 Dat 181   4 33.00
  3   2         -         Datsun 710 Dat  93   4 18.61
  4   3         +         Duster 360 Dus 245   8 15.84
  5   7         +          Merc 240D Mer  62   4 20.00
  6   8         -            Valiant Val 105   6 20.22

패키지에는 빠른 검사를위한 html_output 명령도 있습니다

df_compare $ html_output 여기에 이미지 설명을 입력하십시오


귀하의 compareDF는 정확히 내가 필요한 것입니다. 작은 세트로 좋은 일을했습니다. 그러나 1) 3 열이있는 50Million 행으로 작동하지 않습니다 (예 : 32GB RAM의 메모리 부족이라고 표시됩니다). 2) 또한 HTML을 작성하는 데 시간이 걸리는 것을 알았습니다. 동일한 출력을 TEXT 파일로 보낼 수 있습니까?
Deep

1) 네, 5 천만 행은 메모리에 저장하기 위해 많은 데이터입니다.). 큰 데이터 세트에는 좋지 않다는 것을 알고 있으므로 일종의 청킹을해야 할 수도 있습니다. 2) HTML에 인쇄하지 않도록 인수-limit_html = 0을 지정할 수 있습니다. 동일한 출력은 compare_output $ comparison_df에 있으며 기본 R 함수를 사용하여 CSV / TEXT 파일에 쓸 수 있습니다.
Alex Joseph

@Alex Joseph 님의 답장을 보내 주셔서 감사합니다. 시도해보고 어떻게 진행되는지 알려 드리겠습니다.
Deep

안녕하세요 @Alex Joseph, 텍스트 형식이 작동했지만 문제를 발견 한 입력에 감사드립니다. stackoverflow.com/questions/54880218/…
Deep

다른 수의 열을 처리 할 수 ​​없습니다. 오류가 발생했습니다The two data frames have different columns!
PeyM87

14

당신이 사용할 수있는 daff패키지 (감쌈 daff.js도서관 은 Using V8패키지 ) :

library(daff)

diff_data(data_ref = a2,
          data = a1)

다음과 같은 차이점 개체를 생성합니다.

Daff Comparison: ‘a2’ vs. ‘a1’ 
  First 6 and last 6 patch lines:
   @@   a   b
1 ... ... ...
2       3   c
3 +++   4   d
4 +++   5   e
5 ... ... ...
6 ... ... ...
7       3   c
8 +++   4   d
9 +++   5   e

diff 형식은 표에 대한 Coopy 형광펜 diff 형식으로 설명되어 있으며 매우 자명해야합니다. +++첫 번째 열에 있는 줄 @@은에 새롭고 a1존재하지 않는 줄입니다 a2.

차이점 개체는 다음을 사용하여 patch_data()문서 목적으로 차이점을 저장 write_diff()하거나 다음을 사용하여 차이점render_diff()시각화하는 데 사용할 수 있습니다 .

render_diff(
    diff_data(data_ref = a2,
              data = a1)
)

깔끔한 HTML 출력을 생성합니다.

여기에 이미지 설명을 입력하십시오


10

diffobj패키지 사용 :

library(diffobj)

diffPrint(a1, a2)
diffObj(a1, a2)

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오


10

나는 적응했다 merge 기능을 사용 기능을 . 더 큰 데이터 프레임에서는 전체 병합 솔루션보다 적은 메모리를 사용합니다. 그리고 나는 주요 칼럼의 이름을 가지고 놀 수 있습니다.

또 다른 해결책은 라이브러리를 사용하는 것 prob입니다.

#  Derived from src/library/base/R/merge.R
#  Part of the R package, http://www.R-project.org
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  A copy of the GNU General Public License is available at
#  http://www.r-project.org/Licenses/

XinY <-
    function(x, y, by = intersect(names(x), names(y)), by.x = by, by.y = by,
             notin = FALSE, incomparables = NULL,
             ...)
{
    fix.by <- function(by, df)
    {
        ## fix up 'by' to be a valid set of cols by number: 0 is row.names
        if(is.null(by)) by <- numeric(0L)
        by <- as.vector(by)
        nc <- ncol(df)
        if(is.character(by))
            by <- match(by, c("row.names", names(df))) - 1L
        else if(is.numeric(by)) {
            if(any(by < 0L) || any(by > nc))
                stop("'by' must match numbers of columns")
        } else if(is.logical(by)) {
            if(length(by) != nc) stop("'by' must match number of columns")
            by <- seq_along(by)[by]
        } else stop("'by' must specify column(s) as numbers, names or logical")
        if(any(is.na(by))) stop("'by' must specify valid column(s)")
        unique(by)
    }

    nx <- nrow(x <- as.data.frame(x)); ny <- nrow(y <- as.data.frame(y))
    by.x <- fix.by(by.x, x)
    by.y <- fix.by(by.y, y)
    if((l.b <- length(by.x)) != length(by.y))
        stop("'by.x' and 'by.y' specify different numbers of columns")
    if(l.b == 0L) {
        ## was: stop("no columns to match on")
        ## returns x
        x
    }
    else {
        if(any(by.x == 0L)) {
            x <- cbind(Row.names = I(row.names(x)), x)
            by.x <- by.x + 1L
        }
        if(any(by.y == 0L)) {
            y <- cbind(Row.names = I(row.names(y)), y)
            by.y <- by.y + 1L
        }
        ## create keys from 'by' columns:
        if(l.b == 1L) {                  # (be faster)
            bx <- x[, by.x]; if(is.factor(bx)) bx <- as.character(bx)
            by <- y[, by.y]; if(is.factor(by)) by <- as.character(by)
        } else {
            ## Do these together for consistency in as.character.
            ## Use same set of names.
            bx <- x[, by.x, drop=FALSE]; by <- y[, by.y, drop=FALSE]
            names(bx) <- names(by) <- paste("V", seq_len(ncol(bx)), sep="")
            bz <- do.call("paste", c(rbind(bx, by), sep = "\r"))
            bx <- bz[seq_len(nx)]
            by <- bz[nx + seq_len(ny)]
        }
        comm <- match(bx, by, 0L)
        if (notin) {
            res <- x[comm == 0,]
        } else {
            res <- x[comm > 0,]
        }
    }
    ## avoid a copy
    ## row.names(res) <- NULL
    attr(res, "row.names") <- .set_row_names(nrow(res))
    res
}


XnotinY <-
    function(x, y, by = intersect(names(x), names(y)), by.x = by, by.y = by,
             notin = TRUE, incomparables = NULL,
             ...)
{
    XinY(x,y,by,by.x,by.y,notin,incomparables)
}

7

예제 데이터에는 중복이 없지만 솔루션에서 자동으로 처리합니다. 이는 잠재적으로 일부 답변이 중복 된 경우 함수의 결과와 일치하지 않음을 의미합니다.
다음은 귀하의 것과 동일한 방식으로 주소를 복제하는 솔루션입니다. 또한 확장 성이 뛰어납니다!

a1 <- data.frame(a = 1:5, b=letters[1:5])
a2 <- data.frame(a = 1:3, b=letters[1:3])
rows.in.a1.that.are.not.in.a2  <- function(a1,a2)
{
    a1.vec <- apply(a1, 1, paste, collapse = "")
    a2.vec <- apply(a2, 1, paste, collapse = "")
    a1.without.a2.rows <- a1[!a1.vec %in% a2.vec,]
    return(a1.without.a2.rows)
}

library(data.table)
setDT(a1)
setDT(a2)

# no duplicates - as in example code
r <- fsetdiff(a1, a2)
all.equal(r, rows.in.a1.that.are.not.in.a2(a1,a2))
#[1] TRUE

# handling duplicates - make some duplicates
a1 <- rbind(a1, a1, a1)
a2 <- rbind(a2, a2, a2)
r <- fsetdiff(a1, a2, all = TRUE)
all.equal(r, rows.in.a1.that.are.not.in.a2(a1,a2))
#[1] TRUE

data.table 1.9.8 이상이 필요합니다


2

어쩌면 너무 간단하지만이 솔루션을 사용했으며 데이터 세트를 비교하는 데 사용할 수있는 기본 키가있을 때 매우 유용합니다. 그것이 도움이되기를 바랍니다.

a1 <- data.frame(a = 1:5, b = letters[1:5])
a2 <- data.frame(a = 1:3, b = letters[1:3])
different.names <- (!a1$a %in% a2$a)
not.in.a2 <- a1[different.names,]

OP가 이미 시도한 것과 어떻게 다른가요? 당신은 (요구 사항이었다) 대신 전체 행의 단일 열 비교 탈 같이 동일한 코드를 사용했습니다
데이비드 Arenburg

1

plyr의 match_df를 기반으로 한 또 다른 솔루션입니다. plyr의 match_df는 다음과 같습니다.

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]
}

부정하기 위해 수정할 수 있습니다 :

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]
}

그때:

diff <- negate_match_df(a1,a2)

1

사용 subset:

missing<-subset(a1, !(a %in% a2$a))

이 답변은 OP 시나리오에 적합합니다. 변수 "a"가 두 data.frames ( "a1"과 "a2") 사이에 일치하지만 변수 "b"가 일치하지 않는보다 일반적인 경우는 어떻습니까?
Bryan F

1

다음 코드는 모두 사용 data.tablefastmatch증가 속도.

library("data.table")
library("fastmatch")

a1 <- setDT(data.frame(a = 1:5, b=letters[1:5]))
a2 <- setDT(data.frame(a = 1:3, b=letters[1:3]))

compare_rows <- a1$a %fin% a2$a
# the %fin% function comes from the `fastmatch` package

added_rows <- a1[which(compare_rows == FALSE)]

added_rows

#    a b
# 1: 4 d
# 2: 5 e
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.