Расширенная очистка данных

Как выполнить сопоставление строк нечеткого фрейма данных с помощью RecordLinkage

Элитный пакет для самых сложных задач

Введение

В одной из своих предыдущих статей я писал о том, как выполнить подобие строк для очистки текстовых данных с помощью пакета fuzzywuzzy. Узнать о пакете и применить его на практике было действительно здорово. Но разве не было бы еще лучше, если бы мы могли выполнять тот же процесс между строками фреймов данных?

Собственно, вопрос должен заключаться в том, зачем нам это вообще нужно? Сегодня данные никогда не собираются в одном и том же месте, а в нескольких местах. Распространенной проблемой в этом процессе является преобразование всех небольших фрагментов данных в один и тот же формат, чтобы при их объединении они без проблем работали с программами для обработки данных, такими как SQL или pandas.

Но это не всегда возможно. Рассмотрим эти две фальшивые таблицы:

Предположим, это расписания игр НБА, взятые с разных сайтов. Если мы хотим объединить их вместе, слияние приведет к дублированию, потому что есть нечеткие дубликаты, хотя и не точные:

Чтобы объединить их, вам придется выполнить серьезные операции по очистке данных, чтобы слияние заработало. Однако этот набор данных мог бы состоять из тысяч строк, и вы не смогли бы найти все крайние случаи.

Реальные дела будут намного сложнее. Нечеткое сопоставление строк помогает удалить дубликаты и обеспечивает согласованность ваших данных.

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

Установка и установка

recordlinkage можно установить с помощью pip:

pip install recordlinkage

Чтобы он работал, вам нужно импортировать его с помощью pandas:

Вы можете получить блокнот и данные, использованные в статье об этом репозитории GitHub.

Связывание записей, индексация

В следующих примерах мы загрузим один из встроенных наборов данных recordlinkage, чтобы продемонстрировать его возможности:

Два вышеуказанных набора данных содержат данные переписи, созданные в рамках проекта Febrl. Он был разделен на две части по 5 тысяч строк в каждой, каждая из которых подходит для связывания записей.

Для упрощения иллюстрации я просто возьму случайную выборку из обоих наборов данных:

>>> rand_b

Предположим, мы хотим связать записи двух наборов данных без дублирования. Чтобы начать процесс, нам нужно будет сгенерировать пары для возможных совпадений. Очевидно, мы не можем знать, какие строки совпадают, поэтому нам пришлось бы взять все возможные пары. Создание пар для вычисления сходства выполняется с использованием индексов двух наборов данных. Вот почему его еще называют indexing. Пакет recordlinkage упрощает этот процесс:

Чтобы начать процесс, мы создадим объект индексации. Далее следует указать режим генерации пар. Поскольку нам нужно сгенерировать все возможные комбинации индексов, мы будем использовать метод .full() для объекта индексации:

Затем мы введем наборы данных для генерации пар, также называемых кандидатами, и присвоим результат новой переменной:

Результатом будет pandas.MultiIndex объект. Первый уровень содержит индексы из первого набора данных, и аналогично индексы второго уровня содержат индексы для второго набора данных.

Длина результирующего series всегда будет произведением длин наборов данных. Потому что для наших 5-строчных наборов данных каждый индекс из первой таблицы будет иметь 5 пар индексов из второй:

Однако, если наши наборы данных большие, создание всех возможных пар будет очень затратным с точки зрения вычислений. Чтобы избежать генерации всех возможных пар, мы должны выбрать один столбец, который имеет согласованные значения из обоих наборов данных. Для наших небольших наборов данных есть столбец состояния:

>>> rand_a[['state']], rand_b[['state']]

Если вы обратите внимание, уникальные значения state совпадают в обоих наборах данных. Это означает, что одно название штата не отличается от другого. Это помогает нам, потому что теперь мы можем исключить все пары, у которых нет совпадающего значения состояния. Чтобы сделать это с recordlinkage, мы должны изменить режим с full на blocking:

Помните, логика блокировки для определенного столбца заключается в том, что мы ожидаем, что повторяющиеся значения будут иметь одинаковые или похожие значения в столбцах обоих наборов данных, и если строки не совпадают в каком-то определенном столбце, мы можем исключить эту пару.

Как видите, количество пар (6) значительно сократилось. Эти пары индексов также имеют одинаковые значения для state. Давайте проверим некоторые пары:

Если вы используете блокировку для согласованного общего столбца, количество пар будет намного меньше. Мы даже можем использовать несколько столбцов для блокировки, если уникальные значения этих столбцов несовместимы в обеих таблицах.

Связь с записями, пример из практики

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

Для полных наборов данных возвращается почти 5,5 миллионов пар. Помните, если бы мы использовали полную индексацию, это было бы 25 миллионов. Теперь, используя эти пары кандидатов, мы проведем сравнение значений каждого столбца. Чтобы начать сравнение, мы должны создать объект сравнения:

Этот объект имеет множество полезных функций для сопоставления точных или нечетких значений столбцов. Во-первых, давайте начнем с сопоставления точных совпадений:

Когда мы используем exact для определенных полей, мы ожидаем, что пары строк имеют точно такие же значения для этих полей. Параметры exact:

  • left_on: имя столбца левого набора данных
  • right_on: имя столбца правильного набора данных
  • label: имя столбца результирующего набора данных

После того, как мы выполним все сравнения, результатом будет pandas фрейм данных, а label управляет именем соответствующего столбца в итоговом фрейме данных.

Почему мы выбрали точное соответствие? Поскольку почтовый индекс, идентификатор социального страхования, дата рождения и столбцы состояния должны точно совпадать, чтобы быть дубликатом. Это также зависит от значений этих столбцов. Если уникальные значения согласованы между наборами данных, мы должны использовать exact.

Теперь о нечетком сопоставлении. В столбцах имени, фамилии и адреса, вероятно, будут опечатки и несоответствия, поэтому мы будем использовать для них нечеткое сопоставление строк:

Для нечеткого string сопоставления мы будем использовать метод .string. Параметры для имен столбцов такие же. Прочие параметры:

  • method: управляет алгоритмом, используемым для вычисления сходства строк
  • threshold: порог оценки сходства. Если сходство выше заданного балла, это совпадение.

В зависимости от типа данных существуют и другие методы сопоставления значений: compare.numeric и compare.date.

Теперь, когда у нас есть методы, пришло время вычислить их и присвоить результат переменной:

.compute принимает три аргумента. Первый - это MultiIndex объект потенциальных индексов. Следующие два - это два фрейма данных, которые мы используем. Обратите внимание, что порядок их ввода должен быть таким же, как indexer.index().

После завершения вычислений у нас будет такой набор данных:

>>> matches.sample(5)

Результирующий фрейм данных также имеет многоуровневый индекс, первый - census_a, второй - census_b. В остальных столбцах будет либо 1 для совпадения, либо 0 для несоответствия. Давайте интерпретируем первую строку приведенного выше примера:

Строки с индексами rec-3254-org и rec-1416-dup-0 совпадают только в столбце state, потому что в этом поле 1. Эти строки не соответствуют другим полям.

Теперь давайте установим, когда мы решим, что две строки являются дубликатами. Я думаю, что для нашего набора данных, если строки совпадают по крайней мере в 4 столбцах, очень высока вероятность того, что они дублируются. Мы легко можем подмножество строк с общей оценкой соответствия не менее 4 с sum и логической индексацией:

>>> full_matches.shape
(4676, 7)

Если вы используете .sum() с axis, установленным на 1 или columns, он будет принимать сумму числовых значений по столбцам.

Как видите, из 5,5 миллионов возможных пар совпало почти 4676 строк. Теперь, прежде чем объединить наши исходные таблицы вместе, мы должны убедиться, что мы не включаем эти 4676 строк. Для этого проделаем небольшие манипуляции:

Чтобы получить индексы некоторого уровня из многоуровневых индексов, мы используем .get_level_values на df.index.

Поскольку мы выбрали индекс второго уровня, мы должны исключить их из census_b:

Теперь unique_b готов к добавлению к первому набору данных:

Вот и все. Из 10 тысяч строк, заполненных дубликатами, мы получили 5324 уникальных строки. Вот полный код:

Связь с записями, Пример 2

Чтобы закрепить ваши знания, мы выполним связывание записей с двумя другими наборами данных:

>>> restaurants_1.shape
(82, 5)
>>> restaurants_2.sample(5)

>>> restaurants_2.shape
(336, 5)

Мы должны объединить эти два набора данных без дублирования. Поскольку у них длинные имена и адреса, они, вероятно, будут полны опечаток и несоответствий, поэтому .merge не будет работать должным образом:

Ничего не найдено! Это определенно предполагает, что мы используем связывание записей. Я выполню процесс без особых подробностей, потому что шаги будут такими же, как и раньше:

Если вам понравилась статья, поделитесь ею и оставьте отзыв. Ваша поддержка как писателя значит для меня весь мир!

Прочтите больше статей по теме: