нечеткий поиск между 2 сериями / столбцами неидентичной длины - PullRequest
0 голосов
/ 29 августа 2018

Я пытаюсь сделать нечеткий поиск между 2 рядами / столбцами между df1 и df2, где df1 - это файл словаря (который будет использоваться в качестве основы), а df2 - это целевой файл (для поиска)

import pandas as pd
df1 = pd.DataFrame(data ={'Brand_var':['Altmeister Bitter','Altos Las Hormigas Argentinian Wine','Amadeus Contri Sparkling Wine','Amadeus Cream Liqueur','Amadeus Sparkling Sparkling Wine']})
df2 = pd.DataFrame(data = {'Product':['1960 Altmeister 330ML CAN METAL','Hormi 12 Yr Bottle','test']})

Я искал некоторые решения в SO, к сожалению, похоже, не нашел решения.

Используется:

df3 = df2['ProductLongDesc'].apply(lambda x: difflib.get_close_matches(x, df1['Brand_var'])[0])

также:

df3 = df2['Product'].apply(lambda x: difflib.get_close_matches(x, df1['Brand_var']))

Первый дает мне ошибку индекса, а второй - только индексы.

Мой желаемый вывод - напечатать отображение между элементом df1 и элементами df2, используя нечеткий поиск, и распечатать оба Brand_var и Product для их соответствующих совпадений.

Желаемый вывод:

Brand_var                            Product
Altmeister Bitter                    1960 Altmeister 330ML CAN METAL
Altos Las Hormigas Argentinian Wine  Hormi 12 Yr Bottle

Для несоответствующих элементов, например: test в df2, можно игнорировать.

Примечание. Соответствующее имя строки также может быть не идентичным, так как в нем может отсутствовать 1 или 2 буквы. (

Заранее благодарим вас за то, что нашли время для этой проблемы. :)

1 Ответ

0 голосов
/ 29 августа 2018

Если вы установите fuzzywuzzy, у вас все еще остается проблема, как выбрать правильную эвристику, чтобы выбрать правильный продукт и вырезать те продукты, которые выбраны неправильно (объяснение ниже)

установка fuzzywuzzy:

pip install fuzzywuzzy

fuzzywuzzy имеет несколько методов для вычисления коэффициента ( примеры на github ). Вы сталкиваетесь с проблемой: как выбрать лучшее? Я попробовал их на ваших данных, но все они потерпели неудачу. Код:

import pandas as pd
import numpy as np
from fuzzywuzzy import fuzz

# df1 = ...
# df2 = ...

def get_top_by_ratio(x, df2):
    product_values = df2.Product.values
    # compare two strings by characters
    ratio = np.array([fuzz.partial_ratio(x, val) for val in product_values])
    argmax = np.argmax(ratio)
    rating = ratio[argmax]
    linked_product = product_values[argmax]
    return rating, linked_product

Примените эту функцию к вашим данным:

partial_ratio = (df1.Brand_var.apply(lambda x: get_top_by_ratio(x, df2))
                    .apply(pd.Series)  # convert returned Series of tuples into pd.DataFrame
                    .rename(columns={0: 'ratio', 1: 'Product'}))  # just rename columns
print(partial_ratio)
Out:
0     65  1960 Altmeister 330ML CAN METAL  # Altmeister Bitter 
1     50                             test  # Altos Las Hormigas Argentinian Wine
2     33                             test
3     50                             test
4     50                             test

Это не хорошо. Другие методы отношения, такие как fuzz.ratio, fuzz.token_sort_ratio и т. Д., Тоже потерпели неудачу.

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

def create_vocab(df1, df2):
     # Leave 0 index free for unknow words
    all_words = set((df1.Brand_var.str.cat(sep=' ') + df2.Product.str.cat(sep=' ')).split())
    vocab = dict([(i + 1, w) for i, w in enumerate(all_words)])
    return vocab


def encode(string, vocab):
    """This function encodes a sting with vocabulary"""
    return [vocab[w] if w in vocab else 0 for w in string.split()]

Определить новую эвристику:

def get_top_with_heuristic(x, df2, vocab):
    product_values = df2.Product.values
    # compare two strings by characters
    ratio_per_char = np.array([fuzz.partial_ratio(x, val) for val in product_values])
    # compare two string by words
    ratio_per_word = np.array([fuzz.partial_ratio(x, encode(val, vocab)) for val in product_values])
    ratio = ratio_per_char + ratio_per_word
    argmax = np.argmax(ratio)
    rating = ratio[argmax]
    linked_product = product_values[argmax]
    return rating, linked_product

Создание словарного запаса, применение сложной эвристики к данным:

vocab = create_vocab(df1, df2)
heuristic_rating = (df1.Brand_var.apply(lambda x: get_top_with_heuristic(x, df2, vocab))
                    .apply(pd.Series)
                    .rename(columns={0: 'ratio', 1: 'Product'}))
print(heuristic_rating)
Out: 
   ratio                          Product
0     73  1960 Altmeister 330ML CAN METAL  # Altmeister Bitter 
1     61               Hormi 12 Yr Bottle  # Altos Las Hormigas Argentinian Wine
2     45               Hormi 12 Yr Bottle
3     50                             test
4     50                             test

Кажется, это правильно! Конкатенируйте этот фрейм данных в df1, измените индекс:

result_heuristic = pd.concat((df1, heuristic_rating), axis=1).set_index('Brand_var')
print(result_heuristic)
Out:

                                     ratio                          Product
Brand_var                                                                  
Altmeister Bitter                       73  1960 Altmeister 330ML CAN METAL
Altos Las Hormigas Argentinian Wine     61               Hormi 12 Yr Bottle
Amadeus Contri Sparkling Wine           45               Hormi 12 Yr Bottle
Amadeus Cream Liqueur                   50                             test
Amadeus Sparkling Sparkling Wine        50                             test

Теперь вам нужно выбрать какое-то правило большого пальца, чтобы вырезать неверные данные. Для этого примера ratio <= 50 работает хорошо, но вам, вероятно, понадобятся некоторые исследования, чтобы определить лучший эвристический и правильный порог . Также вы все равно получите некоторые ошибки. Выберите приемлемый коэффициент ошибок, то есть 2%, 5% ... и улучшайте свой алгоритм, пока не достигнете его (эта задача аналогична проверке алгоритмов классификации машинного обучения).

Вырезать неверные "прогнозы":

result = result_heuristic[result_heuristic.ratio > 50][['Product']]
print(result)

Out:                                                        Product
Brand_var                                                           
Altmeister Bitter                    1960 Altmeister 330ML CAN METAL
Altos Las Hormigas Argentinian Wine               Hormi 12 Yr Bottle

Надеюсь, это поможет!

P.S. Конечно, этот алгоритм очень очень медленный, когда вы оптимизируете его, вы должны сделать некоторые оптимизации, например, кэшировать различия и т. Д.

...