2015-01-20 2 views
11

Я работаю с R из фона SAS/SQL и пытаюсь написать код, чтобы взять две таблицы, сравнить их и предоставить список расхождений. Этот код будет использоваться повторно для многих разных наборов таблиц, поэтому мне нужно избегать жесткого кодирования.Найти расхождения между двумя таблицами

Я работаю с Identifying specific differences between two data sets in R, но это не доводит меня до конца.

Пример данные, используя комбинацию LastName/FirstName (который является уникальным) в качестве ключевого -

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 

Обратите внимание, что этот вывод игнорирует запись, где не имеет один и тот же идентификатор в обеих таблицах (например, поскольку фамилия Боба «Дон» в одной таблице, а «Донн» - в другой таблице, мы полностью игнорируем эту запись).

Я исследовал это, применяя функцию расплава на обоих наборах данных, а затем сравнивая их, но данные размера, с которыми я работаю, указывают, что это было бы непрактично. В SAS я использовал Proc Compare для такого рода работ, но я не нашел точного эквивалента в R.

+0

Каков размер ваших данных? Возможно, это интересная информация для включения в ваш вопрос. :) – Bonifacio2

+0

Вы не хотите, чтобы Джон Доу включился в результат? Его имя повторяется в обоих. Или вам просто нужны различия, если они не являются точными дубликатами? –

+0

Размер данных сильно варьируется, но обычно он составляет около 1 миллиона записей или около того. – Netbrian

ответ

7

Вот решение, основанное на 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 

где вход наборы данных:

# 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

Как насчет 'melt (dt1, id.vars = c (" ... "))'? – BrodieG

+0

Из этого: http://stackoverflow.com/a/18450519/1898580, я понял, что 'unlist' может быть более быстрым решением. –

+0

Не тестировали его, но это, скорее всего, использовало медленное' reshape', а не ** [новый добавлен] (https://github.com/Rdatatable/data.table#changes-in-v192-on-cran-27-feb-2014) ** на 'data.table' – BrodieG

6

dplyr и tidyr хорошо работают здесь. Во-первых, немного уменьшенный набор данных:

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) 

(.. К сожалению, я не хочу, чтобы ввести все это в случае, если это важно, пожалуйста, предоставьте reproducible example с четко определенными структурами данных)

Кроме того, похоже, что ваш «желаемый результат» немного отличается от ZIP от Jane Doe и VisitCount.

Ваша мысль их расплавить работает хорошо:

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 

Отсюда, это обманчиво прост:

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

Пакет dataCompareR предназначен для решения этой проблемы. Виньетка для пакета включает в себя несколько простых примеров, и я использовал этот пакет для решения исходной проблемы ниже.

Отказ от ответственности: Я занимался созданием этого пакета.

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 

Чтобы получить более подробное и довольно резюме, пользователь может запустить

summary(compd1d2) 

Использование FIRST_NAME и LAST_NAME как «присоединиться» между двумя таблицами контролируемых keys = аргументом к rCompare функция.В этом случае любые строки, которые не совпадают с этими двумя переменными, исключаются из сравнения, но вы можете получить более подробный результат при сравнении, выполненном с использованием summary

+0

Это очень полезный пакет! Я искал что-то, чтобы подражать команде bash «diff», но без необходимости обращаться к моей ОС. Это соответствует счету! –

Смежные вопросы