Нормализация текста: сходство текста в Python. Как нормализовать несоответствие орфографии текста? - PullRequest
10 голосов
/ 08 мая 2019

У меня есть фрейм данных со столбцом A, как показано ниже:

Column A
Carrefour supermarket
Carrefour hypermarket
Carrefour
carrefour
Carrfour downtown
Carrfor market
Lulu
Lulu Hyper
Lulu dxb
lulu airport
k.m trading
KM Trading
KM trade
K.M.  Trading
KM.Trading

Я хотел бы получить в столбце A ниже:

Column A
Carrefour
Carrefour
Carrefour
Carrefour
Carrefour
Carrefour
Lulu
Lulu
Lulu
Lulu
KM Trading
KM Trading
KM Trading
KM Trading
KM Trading

Для этого я кодкак показано ниже:

MERCHANT_NAME_DICT = {"lulu": "Lulu", "carrefour": "Carrefour",  "km": "KM Trading"}

def replace_merchant_name(row):
    """Provided a long merchant name replace it with short name."""
    processed_row = re.sub(r'\s+|\.', '', row.lower()).strip()
    for key, value in MERCHANT_NAME_DICT.items():
        if key in processed_row:
            return value

    return row

frame['MERCHANT_NAME'] = frame['MERCHANT_NAME'].astype(str)
frame.MERCHANT_NAME = frame.MERCHANT_NAME.apply(lambda row: replace_merchant_name(row))

Но я хотел использовать NLP Logic и сделать ее универсальной функцией (вместо использования значений для отображения).Просто вызовите универсальную функцию и запустите ее для любого аналогичного столбца данных и получите желаемые результаты.Я довольно новичок в NLP Concepts, поэтому ищу какую-то помощь в этом, друзья.

ПРИМЕЧАНИЕ. По сути, я хотел использовать общий способ кодирования NLP, чтобы найти все похожие слова в заданном столбце (или в списке).

Ответы [ 2 ]

4 голосов
/ 08 мая 2019

Вы можете сделать что-то как: для каждого слова, которое не входит в словарь для проверки орфографии (и, следовательно, вероятно, будет написано с ошибкой), загляните в список своих имен торговцев и посмотрите, есть ли имя с маленькимредактировать расстояние.Вы также можете каким-то образом нормализовать слова для поиска по сходству, т. Е. В нижнем регистре все и удалить знаки препинания.

Вы можете использовать textdistance пакет, который реализует множество расстояний между строками.Я бы, вероятно, использовал для этой цели расстояние Джаро-Винклера .

import string
import textdistance

MERCHANT_NAMES = [("lulu", "Lulu"), ("carrefour", "Carrefour"),  ("km", "KM")]
DISTANCE_THRESHOLD = 0.9

def normalize(orig_name):
    name_sig = orig_name.translate(str.maketrans('', '', string.punctuation)).lower()

    best_score = DISTANCE_THRESHOLD
    replacement = name

    for sig, name in MERCHANT_NAMES:
        distance = textdistance.jaro_winkler(name_sig, sig)
        if distance > best_score:
            best_score = distance
            replacement = name
    return replacement

Возможно, вам потребуется настроить приемлемый порог для замены слова и что-то сделать с помощью выражений из нескольких слов.(Например, выбросьте слова, похожие на «супермаркет», «гипермаркет» и т. Д.)

3 голосов
/ 12 мая 2019

Если у вас нет золотого набора «правильных» названий торговцев, это звучит как проблема кластеризации.Это может быть решено с помощью умной функции расстояния (как Jaro-Winkler из ответа Jindrich) и простого алгоритма кластеризации (например, агломерационная кластеризация).

После группировки текстов вы можете найти наиболее представительный текст из каждого кластера и заменить им весь кластер.

import numpy as np
import re
import textdistance
# we will need scikit-learn>=0.21
from sklearn.cluster import AgglomerativeClustering  

texts = [
  'Carrefour supermarket', 'Carrefour hypermarket', 'Carrefour', 'carrefour', 'Carrfour downtown', 'Carrfor market', 
  'Lulu', 'Lulu Hyper', 'Lulu dxb', 'lulu airport', 
  'k.m trading', 'KM Trading', 'KM trade', 'K.M.  Trading', 'KM.Trading'
]

def normalize(text):
  """ Keep only lower-cased text and numbers"""
  return re.sub('[^a-z0-9]+', ' ', text.lower())

def group_texts(texts, threshold=0.4): 
  """ Replace each text with the representative of its cluster"""
  normalized_texts = np.array([normalize(text) for text in texts])
  distances = 1 - np.array([
      [textdistance.jaro_winkler(one, another) for one in normalized_texts] 
      for another in normalized_texts
  ])
  clustering = AgglomerativeClustering(
    distance_threshold=threshold, # this parameter needs to be tuned carefully
    affinity="precomputed", linkage="complete", n_clusters=None
  ).fit(distances)
  centers = dict()
  for cluster_id in set(clustering.labels_):
    index = clustering.labels_ == cluster_id
    centrality = distances[:, index][index].sum(axis=1)
    centers[cluster_id] = normalized_texts[index][centrality.argmin()]
  return [centers[i] for i in clustering.labels_]

print(group_texts(texts))

Приведенный выше код напечатает свой вывод как

['carrefour', 'carrefour', 'carrefour', 'carrefour', 'carrefour', 'carrefour', 
 'lulu', 'lulu', 'lulu', 'lulu', 
 'km trading', 'km trading', 'km trading', 'km trading', 'km trading']

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

  • учитывает синонимы: супермаркет = гипермаркет = рынок
  • лемматизировать слова (так что торговля = торговля)
  • придают меньший весважные слова (IDF?)

К сожалению, большинство таких настроек будут относиться к конкретному домену, поэтому вам придется настроить их на свой собственный набор данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...