Удаление дубликатов на основе нечеткого соответствия в пандах

У меня есть DataFrame с информацией о людях, но есть повторяющиеся строки с немного другим адресом.

Как я могу удалить дубликаты на основе нечеткого соответствия или другого способа обнаружения сходства, но гарантируя, что строка с похожим адресом будет удалена только в том случае, если имя и фамилия также совпадают?

Пример данных:

    First name | Last name | Address
0     John         Doe        ABC 9
1     John         Doe        KFT 2
2     Michael      John       ABC 9
3     Mary         Jane       PEP 9/2
4     Mary         Jane       PEP, 9-2
5     Gary         Young      verylongstreetname 1       
6     Gary         Young      1 verylongstretname

(опечатка в названии улицы намеренно)

Код для примера данных:

df = pd.DataFrame([
    ['John', 'Doe', 'ABC 9'],
    ['John', 'Doe', 'KFT 2'],
    ['Michael', 'John', 'ABC 9'],
    ['Mary', 'Jane', 'PEP 9/2'],
    ['Mary', 'Jane', 'PEP, 9-2'],
    ['Gary', 'Young', 'verylongstreetname 1'],
    ['Gary', 'Young', '1 verylongstretname']
], columns=['First name', 'Last name', 'Address'])

Ожидаемый результат:

    First name | Last name | Address
0     John         Doe        ABC 9
1     John         Doe        KFT 2
2     Michael      John       ABC 9
3     Mary         Jane       PEP 9/2
4     Gary         Young      verylongstreetname 1 

person Sinma    schedule 22.02.2019    source источник
comment
некоторые данные было бы хорошо ..   -  person iamklaus    schedule 22.02.2019
comment
Как уже предлагал @iamklaus, если вы хотите хороших ответов, вы должны правильно задать свой вопрос. В вашем случае нам было бы легче ответить, если вы предоставите примерную таблицу (фрейм данных) и ожидаемый результат также в табличном формате.   -  person Erfan    schedule 22.02.2019
comment
@Sinma Только вы, человек, знаете, что похоже и что является золотым стандартом того, что вы должны сопоставлять свои неаккуратные данные. Не ожидайте, что какой-либо алгоритм машинного обучения сделает эту работу вместо вас. Лучший подход к решению вашей задачи — установить фиксированные правила grep-and-replace, а затем удалить дубликаты.   -  person Sergey Bushmanov    schedule 22.02.2019
comment
@SergeyBushmanov Я был бы доволен удалением дубликатов, например. 90% сходство в адресе с дистанцией Левенштейна что ли. Я написал что-то почти работающее, но ужасное и медленное.   -  person Sinma    schedule 22.02.2019
comment
Если у вас есть 10 похожих кандидатов с выбранной оценкой сходства выше 0,9 (кто сказал, что 0,9 — это правильно, вы перепроверили это?), какой из 10 вы оставите? Я имею в виду, что нужно проделать большую работу, прежде чем использовать fuzzywuzzy, Levinstein, косинус или любую другую оценку сходства.   -  person Sergey Bushmanov    schedule 22.02.2019
comment
@SergeyBushmanov в моем случае не важно, они равны мне и он может быть первым или случайным из этих 10 кандидатов   -  person Sinma    schedule 22.02.2019
comment
Вы можете подумать о (1) векторизации ваших данных с помощью sklearn CountVectorizer (2) вычислении матрицы подобия с cosine расстоянием (3) отбрасывании любых данных, которые имеют сходство в диапазоне 1> сходство> 0,9. Я подозреваю, что для разумного объема данных, скажем, миллионов строк, он должен завершиться в течение нескольких минут.   -  person Sergey Bushmanov    schedule 22.02.2019


Ответы (2)


используйте str.replace, чтобы удалить все несловесные символы, а затем drop_duplicates

df['Address'] = df['Address'].str.replace(r'\W','')
temp_address = df['Address']
df.drop_duplicates(inplace=True)

Вывод

  First name Last name Address
0       John       Doe    ABC9
1       John       Doe    KFT2
2    Michael      John    ABC9
3       Mary      Jane   PEP92

Замена исходного адреса

b['Address'] = b['Address'].apply(lambda x: [w for w in temp_address if w.split(' ')[0] in x][0])

Вывод

 First name Last name  Address
0       John       Doe    ABC 9
1       John       Doe    KFT 2
2    Michael      John    ABC 9
3       Mary      Jane  PEP 9/2

Хорошо, вот способ

df['Address'] = df['Address'].str.replace(r'\W',' ') # giving a space

def check_simi(d):
    temp = []
    flag = 0
    for w in d:
        temp.extend(w.split(' '))
    temp = [t for t in temp if t]    
    flag = len(temp) / 2

    if len(set(temp)) == flag:
        return int(d.index[0])
    else:


indexes = df.groupby(['First name','Last name'])['Address'].apply(check_simi)
indexes = [int(i) for i in indexes if i >= 0]

df.drop(indexes)

  First name Last name               Address
0       John       Doe                 ABC 9
1       John       Doe                 KFT 2
2    Michael      John                 ABC 9
4       Mary      Jane              PEP  9 2
6       Gary     Young  1 verylongstreetname

PS - пожалуйста, загляните на https://github.com/seatgeek/fuzzywuzzy, чтобы найти более чистый подход, я нет, так как моя сеть не позволяет это сделать

person iamklaus    schedule 22.02.2019
comment
мой плохой, я обновил пример данных, показывающий еще один случай, в котором я не думаю, что замена поможет. - person Sinma; 22.02.2019
comment
Спасибо, основываясь на вашем ответе, я создал нечеткий рабочий код, которого я ожидал. - person Sinma; 22.02.2019

Решено.

Основываясь на ответе @iamklaus, я сделал этот фрагмент кода:

def remove_duplicates_inplace(df, groupby=[], similarity_field='', similar_level=85):
    def check_simi(d):
        dupl_indexes = []
        for i in range(len(d.values) - 1):
            for j in range(i + 1, len(d.values)):
                if fuzz.token_sort_ratio(d.values[i], d.values[j]) >= similar_level:
                    dupl_indexes.append(d.index[j])

        return dupl_indexes

    indexes = df.groupby(groupby)[similarity_field].apply(check_simi)

    for index_list in indexes:
        df.drop(index_list, inplace=True)


remove_duplicates_inplace(df, groupby=['firstname', 'lastname'], similarity_field='address')

Выход:

  firstname lastname               address
0      John      Doe                 ABC 9
1      John      Doe                 KFT 2
2   Michael     John                 ABC 9
3      Mary     Jane               PEP 9/2
5      Gary    Young  verylongstreetname 1
person Sinma    schedule 22.02.2019