Связывание записей в R - PullRequest
0 голосов
/ 25 января 2020

Я работаю с большим количеством административных данных (~ 22 миллиона записей) и пытаюсь со временем связать людей. Я знаю, что каждая строка представляет отдельного человека для данного года, но (за некоторыми исключениями, которые я могу использовать для проверки точности своих методов), у меня нет идентификатора, который бы связывал людей с течением времени.

В настоящее время я пытаюсь найти способ выбирать совпадения на основе совпадений по годам. Поэтому, если я увижу Джона Смита в компании A в 2010 и 2011 годах и Джона Смита в компании B в 2012 и 2013 годах, я хочу сказать, что это один и тот же человек. У меня есть код для этого, но он не работает, если в 2010-2013 годах в компании также присутствует Джон Смит C. В качестве дополнительного осложнения, если в компании D есть Джон Смит в 2012 и 2013 годах, то я не хочу связывать Джона Смита в компании A с Джоном Смитом в B или D.

Я думаю, что Я хочу сделать, чтобы свернуть данные по полям имен и создать списки лет, а затем сравнить, являются ли списки смежными по компаниям. До сих пор я использовал подход group_by, но я открыт для других предложений.

Мне нужно будет запустить этот код только один раз, чтобы создать ссылки, поэтому эффективность - не самая важная проблема. Это игрушечный пример, в моем конкретном случае я использую намного больше полей и несколько уровней прогрессивно менее ограничительных правил сопоставления.

Большинство нечетких сопоставлений / примеров, которые я видел, основаны на связывании двух наборы данных вместе, и я мог бы разделить свои данные на один набор данных в год (~ 20 лет), но это кажется неправильным подходом.

# Testing data
library(dplyr)
library(tibble)


test <- tribble(
  ~year, ~first, ~last, ~company,
  2010, 'john', 'smith', 'a',
  2011, 'john', 'smith', 'a',  
  2012, 'john', 'smith', 'b', 
  2013, 'john', 'smith', 'b',
  2010, 'john', 'smith', 'c',
  2011, 'john', 'smith', 'c',
  2012, 'john', 'smith', 'c',
  2013, 'john', 'smith', 'c',
  2012, 'john', 'smith', 'd',
  2013, 'john', 'smith', 'd'
) 

test$index <- 1:nrow(test)


# Simple case matching
test1 <- filter(test, company %in% c("a", "b"))

# Match on all fields, keeps John's separate
test1 %>% 
  group_by(first, last, company) %>% 
  mutate(id = if_else(!anyDuplicated(year), min(index), 0L)) %>% 
  ungroup()

# Some fields, partial match, Johns are linked
test1 %>% 
  group_by(first, last) %>% 
  mutate(id = if_else(!anyDuplicated(year), min(index), 0L)) %>% 
  ungroup()

# Confounding Group, partial match gets blocked
test2 <- test %>% filter(company %in% c("a", "b", "c"))

test2 %>% 
  group_by(first, last) %>% 
  mutate(id = if_else(!anyDuplicated(year), min(index), 0L)) %>% 
  ungroup()

# Potential Approach
test2 %>% 
  group_by(first, last, company) %>% 
  summarize(year = list(year))
# check if lists are contiguous, if so link??

# If multiple potential matches, shouldn't link
# full test set includes company d, now cant link john smith from a to c

1 Ответ

0 голосов
/ 27 января 2020

Короткий ответ таков: при существующей структуре данных year, name и company вы можете с уверенностью связываться только в том случае, если имя человека уникально во всем наборе данных.

Примите во внимание следующее :

test <- tribble(
  ~year, ~first, ~last, ~company, interpretation 1, interpretation 2, interpretation 3
  2010, 'john', 'smith', 'a',             1                 1                 1
  2011, 'john', 'smith', 'a',             1                 1                 2
  2010, 'john', 'smith', 'b',             2                 2                 3
  2011, 'john', 'smith', 'b',             2                 2                 4
  2012, 'john', 'smith', 'b',             2                 2                 5
  2013, 'john', 'smith', 'b',             2                 2                 6
  2013, 'john', 'smith', 'c',             2                 3                 7
) 

Как написано, вы не можете сказать, является ли это

  • Интерпретация 1: 2 Джонс, тот, кто работал на двух работах в 2013 году
  • Интерпретация 2: 3 Джона, каждый, кто работал в другой компании
  • Интерпретация 3: 7 Джонс, каждый из которых работал один год

Это было бы лучше

Гораздо сильнее формат будет иметь даты начала и окончания:

start_date, end_date,   name, company
2010-01-01, 2011-12-01, john,  a
2010-01-01, 2013-12-01, john,  b
2013-01-01, 2013-12-01, john,  c

Это дает понять, что это не может быть интерпретация 1 или 3 (хотя есть и другие варианты, поэтому это может быть и интерпретация 2).

Отсюда вы можете сделать что-то вроде следующего, чтобы определить, сколько других записей может связать каждая запись:

test %>%
  inner_join(test, by = 'name', suffix = c("","_c") %>% # suffix _c for comparison
  filter(end_date < start_date_c | end_date_c < start_date) %>% # periods do not overlap
  group_by(start_date, end_date, name, company) %>%
  summarize(num_possible_links = n_dist(start_date_c))

Тогда любая запись с num_possible_links != 1 не может быть связана. Это связано с тем, что либо num_possible_links = 0 и не с чем связываться, либо с num_possible_links > 1, поэтому существует несколько вариантов, и один из них нельзя выбрать с уверенностью.

Затем вы можете использовать очень похожий код, указанный выше, для ссылки вместе тождества, где num_possible_links = 1.

Но это не идеально

Но в этот момент вы сталкиваетесь с еще одним препятствием: насколько велик разрыв в занятости? Предположим, ваши данные выглядят так:

start_date, end_date,   name, company
2010-01-01, 2010-12-01, john,  a
2011-01-01, 2011-12-01, john,  b
2012-01-01, 2012-12-01, john,  c

Это может быть один и тот же Джон в трех разных компаниях. Но это также могут быть 2 Джона, один, который работал в компании b в течение года, а другой, который работал в компании a и c, находясь за границей / безработный в середине.

Без каких-либо дополнительных переменных связать, или некоторые дополнительные предположения, выход за пределы этой точки будет очень трудным.

...