डेटा में पंक्तियों को खोजने के लिए दो डेटा.फ्रेम की तुलना करें। डेटा 1 में मौजूद नहीं हैं। 2


161

मेरे पास निम्नलिखित 2 data.frames है:

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

मैं a1 है कि a2 नहीं है पंक्ति खोजना चाहता हूँ।

क्या इस प्रकार के ऑपरेशन के लिए एक अंतर्निहित फ़ंक्शन है?

(पीएस: मैंने इसके लिए एक समाधान लिखा था, मैं बस उत्सुक हूं कि अगर कोई पहले से ही एक और अधिक कोड बनाया गया है)

यहाँ मेरा समाधान है:

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

यह सीधे आपके प्रश्न का उत्तर नहीं देता है, लेकिन यह आपको उन तत्वों को देगा जो आम हैं। यह पॉल मुर्रे के पैकेज के साथ किया जा सकता है 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वही छोड़ दो । अब तुलना करके देखें। केवल सामान्य तत्वों को सूचीबद्ध करने के लिए उचित तरीका क्या विकल्प है यह पढ़ने में भी मेरे लिए स्पष्ट नहीं है।
हेन्डी

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_joinऔर semi_join!
drastega

क्या कोई कारण है कि एंटी_जॉइन एक सुस्त DF को वापस करेगा, जैसा कि sqldf होगा, लेकिन फ़ंक्शन समान (a1, a2) और all.equal () विरोधाभास होगा?
3pitt

बस यहाँ जोड़ना चाहता था कि एंटी_जॉइन और सेमी_जोइन कुछ मामलों में मेरा काम नहीं करेगा। मुझे अपने डेटा फ्रेम के लिए "त्रुटि: कॉलम 1d परमाणु वैक्टर या सूचियां" होना चाहिए था। शायद मैं अपने डेटा को संसाधित कर सकता हूं ताकि ये कार्य काम करें। Sqldf ने गेट के ठीक बाहर काम किया!
अक्षय गौड़

@ अक्षयगौर यह सिर्फ एक डेटा प्रारूप या डेटा सफाई की समस्या होनी चाहिए; sqldf सिर्फ sql है सब कुछ nromal DB की तरह होने के लिए पूर्व-संसाधित है जैसे कि हम डेटा पर सिर्फ sql चला सकते हैं।
stucash

75

में dplyr :

setdiff(a1,a2)

मूल रूप से, setdiff(bigFrame, smallFrame)आपको पहली तालिका में अतिरिक्त रिकॉर्ड मिलते हैं।

SQLverse में इसे a कहा जाता है

लेफ्ट को छोड़कर वेन डायग्राम ज्वाइन करें

सभी जॉइन ऑप्शंस और सेट सब्जेक्ट्स के अच्छे विवरण के लिए, यह एक सबसे अच्छा सारांश है जिसे मैंने आज तक एक साथ देखा है: http://www.vertabelo.com/blog/technical-articles/sql-joins

लेकिन इस सवाल पर वापस - यहाँ setdiff()ओपी के डेटा का उपयोग करते समय कोड के लिए परिणाम हैं :

> 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'))? "रिकार्ड" के उत्तर में, मैं देखता हूं कि semi_joinसुझाव दिया गया था।
स्टीवेब

ज़रूर! एक और बढ़िया विकल्प, भी; विशेष रूप से यदि आपके पास केवल एक सम्मिलित कुंजी और भिन्न कॉलम नामों के साथ डेटाफ्रेम हैं।
लीरसेज

setdiff lubridate से है :: setdiff और लाइब्रेरी से नहीं (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
लुब्रिडेट और ड्रीलर के बीच संघर्ष है, देखें github.com/tidyverse/lubridate/issues/693
slhck

39

यह निश्चित रूप से इस विशेष उद्देश्य के लिए कुशल नहीं है, लेकिन मैं अक्सर इन स्थितियों में क्या करता हूं प्रत्येक डेटा में संकेतक चर सम्मिलित करना है। फिर विलय करें:

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

शामिल_a1 में अनुपलब्ध मान नोट करेंगे कि कौन सी पंक्तियाँ a1 में गायब हैं। इसी तरह a2 के लिए।

आपके समाधान के साथ एक समस्या यह है कि कॉलम के आदेश मेल खाने चाहिए। एक और समस्या यह है कि ऐसी स्थितियों की कल्पना करना आसान है जहां पंक्तियों को समान रूप से कोडित किया जाता है जब वास्तव में भिन्न होते हैं। मर्ज का उपयोग करने का लाभ यह है कि आपको मुफ्त में सभी त्रुटि की जांच मिलती है जो एक अच्छे समाधान के लिए आवश्यक है।


तो ... एक लापता मूल्य की तलाश में, आप एक और लापता मूल्य बनाते हैं ... आप लापता मूल्य (ओं) को कैसे ढूंढते हैं included_a1? : - /
लुई मैडॉक्स

1
उपयोग is.na () और सबसेट, या dplyr :: फ़िल्टर
एडुआर्डो लियोनी

एक नया पुस्तकालय स्थापित किए बिना एक रास्ता सिखाने के लिए धन्यवाद!
रॉड्रिगो

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 यहां छवि विवरण दर्ज करें


आपकी तुलना डीडीएफ को वास्तव में वही है जो मुझे चाहिए, और छोटे सेटों के साथ एक अच्छा काम किया है। हालांकि: 1) 3 कॉलम (कहो) के साथ एक सेट 50Million पंक्तियों के साथ काम नहीं कर रहा है, यह 32 जीबी रैम के साथ मेमोरी से बाहर कहता है। 2) मैं यह भी देखता हूं कि HTML को लिखने में कुछ समय लगता है, क्या उसी आउटपुट को TEXT फाइल में भेजा जा सकता है?
दीप

1) हाँ 50 मिलियन पंक्तियाँ डेटा का एक बहुत है, बस स्मृति में रखने के लिए;)। मुझे पता है कि यह बड़े डेटासेट्स के साथ बहुत अच्छा नहीं है, इसलिए आपको किसी तरह का मंथन करना पड़ सकता है। 2) आप तर्क दे सकते हैं - limit_html = 0, इसे HTML में प्रिंट करने से बचने के लिए। समान आउटपुट तुलना_आउटपुट $ Compar_df में है जिसे आप मूल R फ़ंक्शन का उपयोग करके CSV / TEXT के लिए लिख सकते हैं।
एलेक्स जोसेफ

आपके उत्तर के लिए धन्यवाद @ एलेक्स जोसेफ, मैं इसे एक कोशिश दूंगा और आपको बता दूंगा कि यह कैसे होता है।
दीप

हाय @ एलेक्स जोसेफ, इनपुट के लिए धन्यवाद पाठ प्रारूप ने काम किया, लेकिन एक मुद्दा मिला, इसके तहत उठाया: stackoverflow.com/questions/54880218/…
दीप

यह स्तंभों की विभिन्न संख्याओं को संभाल नहीं सकता है। मुझे एक त्रुटि मिलीThe two data frames have different columns!
PeyM87

14

आप daffपैकेज का उपयोग कर सकते हैं (जो पैकेज का उपयोग करके daff.jsपुस्तकालय को लपेटता है ):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

कोरी हाइलाइटर में तालिकाओं के लिए अलग प्रारूप का वर्णन किया गया है और इसे बहुत आत्म-व्याख्यात्मक होना चाहिए। +++पहले कॉलम वाली लाइनें @@वे हैं जो नए हैं 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

इसके लिए डेटा की जरूरत है। 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,]

यह कैसे अलग है जो ओपी पहले से ही कोशिश कर रहा है? आपने पूरी पंक्ति के बजाय एकल कॉलम की तुलना करने के लिए ताल जैसे सटीक समान कोड का उपयोग किया है (जो कि आवश्यकता थी)
डेविड अरेनबर्ग

1

अभी तक plyr में match_df पर आधारित एक और समाधान। यहाँ है plyr मिलान_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))

यह उत्तर ओपी के परिदृश्य के लिए काम करता है। अधिक सामान्य मामले के बारे में क्या है जब चर "a" दो data.frames ("a1" और "a2") के बीच मेल खाता है, लेकिन चर "b" नहीं करता है?
ब्रायन एफ

1

निम्न कोड दोनों का उपयोग करता है data.tableऔर fastmatchबढ़ी हुई गति के लिए।

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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.