2015-01-20 19 views
11

Sto lavorando con R da uno sfondo SAS/SQL e sto cercando di scrivere codice per prendere due tabelle, confrontarle e fornire un elenco delle discrepanze. Questo codice dovrebbe essere usato ripetutamente per molti diversi gruppi di tabelle, quindi ho bisogno di evitare hardcoding.Trova discrepanze tra due tabelle

Sto lavorando con Identifying specific differences between two data sets in R, ma non mi arriva fino in fondo.

Esempio Dati, utilizzando la combinazione del cognome/FirstName (che è unico) come chiave -

Dataset One -- 

Last_Name First_Name Street_Address ZIP  VisitCount 
Doe  John  1234 Main St  12345 20 
Doe  Jane  4321 Tower St 54321 10 
Don  Bob   771 North Ave 23232 5 
Smith  Mike  732 South Blvd. 77777 3   

Dataset Two -- 

Last_Name First_Name Street_Address ZIP  VisitCount 
Doe  John  1234 Main St  12345 20 
Doe  Jane  4111 Tower St 32132 17 
Donn  Bob   771 North Ave 11111 5 

    Desired Output -- 

    LastName FirstName VarName   TableOne  TableTwo 
    Doe  Jane  StreetAddress 4321 Tower St 4111 Tower St 
    Doe  Jane  Zip    23232   32132 
    Doe  Jane  VisitCount  5    17 

Si noti che questa uscita ignora i record in cui non ho lo stesso ID in entrambe le tabelle (ad esempio, poiché il cognome di Bob è "Don" in una tabella e "Donn" in un'altra tabella, ignoriamo completamente tale record).

Ho esplorato questo facendo applicando la funzione di fusione su entrambi i set di dati, e poi confrontandoli, ma i dati di dimensione con cui sto lavorando indicano che non sarebbe pratico. In SAS, ho usato Proc Compare per questo tipo di lavoro, ma non ho trovato un equivalente esatto in R.

+0

Qual è la dimensione dei dati? Forse è un'interessante informazione da includere nella tua domanda. :) – Bonifacio2

+0

Non vuoi includere John Doe nel risultato? Il suo nome si ripete in entrambi. O vuoi solo le differenze se non sono duplicati esatti? –

+0

La dimensione dei dati varia molto, ma in genere supera a circa 1 milione di record. – Netbrian

risposta

7

Qui è una soluzione basata su data.table:

library(data.table) 

# Convert into data.table, melt 
setDT(d1) 
d1 <- d1[, list(VarName = names(.SD), TableOne = unlist(.SD, use.names = F)),by=c('Last_Name','First_Name')] 

setDT(d2) 
d2 <- d2[, list(VarName = names(.SD), TableTwo = unlist(.SD, use.names = F)),by=c('Last_Name','First_Name')] 

# Set keys for merging 
setkey(d1,Last_Name,First_Name,VarName) 

# Merge, remove duplicates 
d1[d2,nomatch=0][TableOne!=TableTwo] 

#  Last_Name First_Name  VarName  TableOne  TableTwo 
#  1:  Doe  Jane Street_Address 4321 Tower St 4111 Tower St 
#  2:  Doe  Jane   ZIP   54321   32132 
#  3:  Doe  Jane  VisitCount   10   17 

cui ingresso set di dati sono:

# Input Data Sets 
d1 <- structure(list(Last_Name = c("Doe", "Doe", "Don", "Smith"), First_Name = c("John", 
"Jane", "Bob", "Mike"), Street_Address = c("1234 Main St", "4321 Tower St", 
"771 North Ave", "732 South Blvd."), ZIP = c(12345L, 54321L, 
23232L, 77777L), VisitCount = c(20L, 10L, 5L, 3L)), .Names = c("Last_Name", 
"First_Name", "Street_Address", "ZIP", "VisitCount"), class = "data.frame", row.names = c(NA, -4L))                            

d2 <- structure(list(Last_Name = c("Doe", "Doe", "Donn"), First_Name = c("John", 
"Jane", "Bob"), Street_Address = c("1234 Main St", "4111 Tower St", 
"771 North Ave"), ZIP = c(12345L, 32132L, 11111L), VisitCount = c(20L, 
17L, 5L)), .Names = c("Last_Name", "First_Name", "Street_Address", 
"ZIP", "VisitCount"), class = "data.frame", row.names = c(NA, -3L)) 
+0

Che ne dite di 'melt (dt1, id.vars = c (" ... "))'? – BrodieG

+0

Da questo: http://stackoverflow.com/a/18450519/1898580, ho avuto l'idea che 'unlist' potrebbe essere una soluzione più veloce –

+0

Non l'ho provato, ma probabilmente usava il' reshape' lento, non il ** [nuovo aggiunto] (https://github.com/Rdatatable/data.table#changes-in-v192-on-cran-27-feb-2014) ** a 'data.table' – BrodieG

6

dplyr e tidyr funzionano bene qui. In primo luogo, un set di dati leggermente ridotta:

dat1 <- data.frame(Last_Name = c('Doe', 'Doe', 'Don', 'Smith'), 
        First_Name = c('John', 'Jane', 'Bob', 'Mike'), 
        ZIP = c(12345, 54321, 23232, 77777), 
        VisitCount = c(20, 10, 5, 3), 
        stringsAsFactors = FALSE) 
dat2 <- data.frame(Last_Name = c('Doe', 'Doe', 'Donn'), 
        First_Name = c('John', 'Jane', 'Bob'), 
        ZIP = c(12345, 32132, 11111), 
        VisitCount = c(20, 17, 5), 
        stringsAsFactors = FALSE) 

(.. Mi dispiace, non volevo scrivere tutto in Se è importante, si prega di fornire un reproducible example con strutture di dati ben definite)

Inoltre, sembra che il tuo "output desiderato" sia leggermente inferiore a quello di Jane Doe ZIP e VisitCount.

Il vostro pensiero a sciogliersi loro funziona bene:

library(dplyr) 
library(tidyr) 
dat1g <- gather(dat1, key, value, -Last_Name, -First_Name) 
dat2g <- gather(dat2, key, value, -Last_Name, -First_Name) 
head(dat1g) 
## Last_Name First_Name  key value 
## 1  Doe  John  ZIP 12345 
## 2  Doe  Jane  ZIP 54321 
## 3  Don  Bob  ZIP 23232 
## 4  Smith  Mike  ZIP 77777 
## 5  Doe  John VisitCount 20 
## 6  Doe  Jane VisitCount 10 

Da qui, è ingannevolmente semplice:

dat1g %>% 
    inner_join(dat2g, by = c('Last_Name', 'First_Name', 'key')) %>% 
    filter(value.x != value.y) 
## Last_Name First_Name  key value.x value.y 
## 1  Doe  Jane  ZIP 54321 32132 
## 2  Doe  Jane VisitCount  10  17 
2

Il pacchetto dataCompareR ha lo scopo di risolvere questo problema esatto. La vignetta del pacchetto include alcuni semplici esempi e ho utilizzato questo pacchetto per risolvere il problema originale di seguito.

Disclaimer: Sono stato coinvolto nella creazione di questo pacchetto.

library(dataCompareR) 

d1 <- structure(list(Last_Name = c("Doe", "Doe", "Don", "Smith"), First_Name = c("John", "Jane", "Bob", "Mike"), Street_Address = c("1234 Main St", "4321 Tower St", "771 North Ave", "732 South Blvd."), ZIP = c(12345L, 54321L, 23232L, 77777L), VisitCount = c(20L, 10L, 5L, 3L)), .Names = c("Last_Name", "First_Name", "Street_Address", "ZIP", "VisitCount"), class = "data.frame", row.names = c(NA, -4L))                            

d2 <- structure(list(Last_Name = c("Doe", "Doe", "Donn"), First_Name = c("John", "Jane", "Bob"), Street_Address = c("1234 Main St", "4111 Tower St", "771 North Ave"), ZIP = c(12345L, 32132L, 11111L), VisitCount = c(20L, 17L, 5L)), .Names = c("Last_Name", "First_Name", "Street_Address", "ZIP", "VisitCount"), class = "data.frame", row.names = c(NA, -3L)) 

compd1d2 <- rCompare(d1, d2, keys = c("First_Name", "Last_Name")) 

print(compd1d2) 

All columns were compared, 3 row(s) were dropped from comparison 
There are 3 mismatched variables: 
First and last 5 observations for the 3 mismatched variables 
FIRST_NAME LAST_NAME  valueA  valueB  variable  typeA typeB diffAB 
1  Jane  Doe 4321 Tower St 4111 Tower St STREET_ADDRESS character character  
2  Jane  Doe   10   17  VISITCOUNT integer integer  -7 
3  Jane  Doe   54321   32132   ZIP integer integer 22189 

Per ottenere un riepilogo più dettagliato e bella, l'utente può eseguire

summary(compd1d2) 

L'uso di FIRST_NAME e COGNOME come il 'join' tra le due tabelle è controllato dal keys = argomento al rCompare funzione.In questo caso tutte le righe che non corrispondono a queste due variabili vengono eliminate dal confronto, ma puoi ottenere un risultato più dettagliato sul confronto eseguito utilizzando summary

+0

Questo è un pacchetto molto utile! Stavo cercando qualcosa per imitare il comando bash "diff", ma senza dover chiamare il mio sistema operativo. Questo si adatta al conto! –

Problemi correlati