Строки Dataframe совпадают друг с другом в сходстве TF-IDF Cosine i - PullRequest
1 голос
/ 29 февраля 2020

Я пытаюсь изучить науку о данных и нашел эту замечательную статью в Интернете.

https://bergvca.github.io/2017/10/14/super-fast-string-matching.html

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

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

import pandas as pd
import re
from sklearn.feature_extraction.text import TfidfVectorizer
import numpy as np
from scipy.sparse import csr_matrix
import sparse_dot_topn.sparse_dot_topn as ct

pd.set_option('display.max_colwidth', -1)
df = pd.read_csv('CSV/Contacts.csv',  dtype=str)
print(df.shape)
df.head(2)

Форма: (72489, 3)

    Id  Name    Email
0   0031J00001bvXFTQA2 FRESHPOINT ATLANTA, INC  dotcomp@sysco.com
1   0031J00001aJtFaQAK  VIRGIL  dotcom@corp.sysco.com

Затем я очищаю данные

# Clean the data
df.dropna()
# df['Email'] = df['Email'].str.replace('[^a-zA-Z]', '')
# df['Email'] = df['Email'].str.replace(r'[^\w\s]+', '')

contact_emails = df['Email']

Затем я реализую функцию N-граммы

def ngrams(string, n=3):
    string = re.sub(r'[,-./]|\sBD',r'', string)
    ngrams = zip(*[string[i:] for i in range(n)])
    return [''.join(ngram) for ngram in ngrams]

Затем я получаю матрицу TF-IDF

# get Tf-IDF Matrix
vectorizer = TfidfVectorizer(min_df=1, analyzer=ngrams)
tf_idf_matrix = vectorizer.fit_transform(contact_emails.apply(lambda x: np.str_(x)))

Затем я реализую функцию сходства косинусов - которой я до сих пор не совсем уверен, что делает каждый параметр.

def awesome_cossim_top(A, B, ntop, lower_bound=0):
    # force A and B as a CSR matrix.
    # If they have already been CSR, there is no overhead
    A = A.tocsr()
    B = B.tocsr()
    M, _ = A.shape
    _, N = B.shape

    idx_dtype = np.int32

    nnz_max = M*ntop

    indptr = np.zeros(M+1, dtype=idx_dtype)
    indices = np.zeros(nnz_max, dtype=idx_dtype)
    data = np.zeros(nnz_max, dtype=A.dtype)

    ct.sparse_dot_topn(
        M, N, np.asarray(A.indptr, dtype=idx_dtype),
        np.asarray(A.indices, dtype=idx_dtype),
        A.data,
        np.asarray(B.indptr, dtype=idx_dtype),
        np.asarray(B.indices, dtype=idx_dtype),
        B.data,
        ntop,
        lower_bound,
        indptr, indices, data)

    return csr_matrix((data,indices,indptr),shape=(M,N))

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

matches = awesome_cossim_top(tf_idf_matrix, tf_idf_matrix.transpose(), 10, 0.7)

Тогда вот функция для извлечения совпадений.

def get_matches_df(sparse_matrix, email_vector,email_ids, top=5):
    non_zeros = sparse_matrix.nonzero()

    sparserows = non_zeros[0]
    sparsecols = non_zeros[1]


    if top:
        nr_matches = top
    else:
        nr_matches = sparsecols.size
    left_name_Ids = np.empty([nr_matches], dtype=object)
    right_name_Ids = np.empty([nr_matches], dtype=object)

    left_side = np.empty([nr_matches], dtype=object)
    right_side = np.empty([nr_matches], dtype=object)
    similairity = np.zeros(nr_matches)

    for index in range(nr_matches):        
        left_name_Ids[index] = email_ids[sparserows[index]]
        left_side[index] = email_vector[sparserows[index]]

        right_name_Ids[index] = email_ids[sparsecols[index]]
        right_side[index] = email_vector[sparsecols[index]]
        similairity[index] = sparse_matrix.data[index]

    return pd.DataFrame({
                        'SFDC_ID':  left_name_Ids,
                        'left_side': left_side,
                        'right_SFDC_ID':right_name_Ids,
                          'right_side': right_side,
                           'similairity': similairity})

Затем я вызываю функцию и передать параметры

name_Ids = df['Id']
matches_df = get_matches_df(matches, contact_emails,name_Ids, top=72489)

Теперь я хочу извлечь только совпадения, которые на 90% похожи или более.

matches_df = matches_df[matches_df['similairity'] > 0.9] 

Затем я сортирую значения по сходству

matches_df.sort_values('similairity' )

enter image description here

Итак, я обнаружил, что одни и те же строки сопоставляются друг с другом. Я знаю это, потому что идентификаторы SFD C точно такие же. Почему это происходит? Как я могу избежать этого в будущем? Я, очевидно, не хочу, чтобы строка оценивала себя при нахождении сходства.

...