Расширенная очистка данных
Как выполнить сопоставление строк нечеткого фрейма данных с помощью 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
не будет работать должным образом:
Ничего не найдено! Это определенно предполагает, что мы используем связывание записей. Я выполню процесс без особых подробностей, потому что шаги будут такими же, как и раньше:
Если вам понравилась статья, поделитесь ею и оставьте отзыв. Ваша поддержка как писателя значит для меня весь мир!
Прочтите больше статей по теме: