Programing

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

lottogame 2020. 6. 12. 22:09
반응형

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


다음과 같은 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)

이것은 귀하의 질문에 직접 대답하지는 않지만 공통적 인 요소를 제공합니다. 이것은 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

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)

에서 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


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

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

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

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


나는 같은 문제가 있었기 때문에 패키지 ( 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 여기에 이미지 설명을 입력하십시오


당신이 사용할 수있는 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 출력을 생성합니다.

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


diffobj패키지 사용 :

library(diffobj)

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

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

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


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

Your example data does not have any duplicates, but your solution handle them automatically. This means that potentially some of the answers won't match to results of your function in case of duplicates.
Here is my solution which address duplicates the same way as yours. It also scales great!

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

It needs data.table 1.9.8+


Maybe it is too simplistic, but I used this solution and I find it very useful when I have a primary key that I can use to compare data sets. Hope it can help.

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

Yet another solution based on match_df in plyr. Here's plyr's 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]
}

We can modify it to negate:

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

Then:

diff <- negate_match_df(a1,a2)

Using subset:

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

참고URL : https://stackoverflow.com/questions/3171426/compare-two-data-frames-to-find-the-rows-in-data-frame-1-that-are-not-present-in

반응형