我必须根据名称对某些数据进行交叉验证。
我面临的问题是,根据来源,名称会略有不同,例如:
L & L AIR CONDITIONING vs L & L AIR CONDITIONING Service
BEST ROOFING vs ROOFING INC
我有几千条记录,所以手动操作会非常耗时,我希望尽可能自动化这个过程。
由于还有其他单词,因此小写名称是不够的。
哪个是处理此问题的好算法?
可能要计算相关性,给予“INC”或“服务”等词语低权重
编辑:
我尝试了difflib库
difflib.SequenceMatcher(None,name_1.lower(),name_2.lower()).ratio()
我用它得到了不错的结果。
答案 0 :(得分:5)
我会用余弦相似来达到同样的目的。它将为您提供匹配字符串的匹配分数。
以下代码可以帮助您(我记得几个月前从Stackoverflow本身获取此代码 - 现在找不到链接)
import re, math
from collections import Counter
WORD = re.compile(r'\w+')
def get_cosine(vec1, vec2):
# print vec1, vec2
intersection = set(vec1.keys()) & set(vec2.keys())
numerator = sum([vec1[x] * vec2[x] for x in intersection])
sum1 = sum([vec1[x]**2 for x in vec1.keys()])
sum2 = sum([vec2[x]**2 for x in vec2.keys()])
denominator = math.sqrt(sum1) * math.sqrt(sum2)
if not denominator:
return 0.0
else:
return float(numerator) / denominator
def text_to_vector(text):
return Counter(WORD.findall(text))
def get_similarity(a, b):
a = text_to_vector(a.strip().lower())
b = text_to_vector(b.strip().lower())
return get_cosine(a, b)
get_similarity('L & L AIR CONDITIONING', 'L & L AIR CONDITIONING Service') # returns 0.9258200997725514
我觉得有用的另一个版本是基于NLP的,我创作了它。
import re, math
from collections import Counter
from nltk.corpus import stopwords
from nltk.stem.porter import *
from nltk.corpus import wordnet as wn
stop = stopwords.words('english')
WORD = re.compile(r'\w+')
stemmer = PorterStemmer()
def get_cosine(vec1, vec2):
# print vec1, vec2
intersection = set(vec1.keys()) & set(vec2.keys())
numerator = sum([vec1[x] * vec2[x] for x in intersection])
sum1 = sum([vec1[x]**2 for x in vec1.keys()])
sum2 = sum([vec2[x]**2 for x in vec2.keys()])
denominator = math.sqrt(sum1) * math.sqrt(sum2)
if not denominator:
return 0.0
else:
return float(numerator) / denominator
def text_to_vector(text):
words = WORD.findall(text)
a = []
for i in words:
for ss in wn.synsets(i):
a.extend(ss.lemma_names())
for i in words:
if i not in a:
a.append(i)
a = set(a)
w = [stemmer.stem(i) for i in a if i not in stop]
return Counter(w)
def get_similarity(a, b):
a = text_to_vector(a.strip().lower())
b = text_to_vector(b.strip().lower())
return get_cosine(a, b)
def get_char_wise_similarity(a, b):
a = text_to_vector(a.strip().lower())
b = text_to_vector(b.strip().lower())
s = []
for i in a:
for j in b:
s.append(get_similarity(str(i), str(j)))
try:
return sum(s)/float(len(s))
except: # len(s) == 0
return 0
get_similarity('I am a good boy', 'I am a very disciplined guy')
# Returns 0.5491201525567068
您可以同时致电get_similarity
或get_char_wise_similarity
,了解哪些内容适合您的用例。我使用了两者 - 正常的相似性与非常接近的杂草,然后是明显的相似性,以排除足够接近的相似性。其余的必须手动处理。
答案 1 :(得分:3)
您可以只使用Levenshtein distance,这是计算两个字符串之间差异的好方法。
答案 2 :(得分:2)
您需要弄清楚名称相似的含义。 轻微的拼写差异很难 - 我不会专注于此。
我们假设您有三种变体:
- 大写/小写
- 附加词语
- 标点与空格
我建议将每个字符串拆分成单词,无论它们是用连字符,空格,句点等分隔开。将每个字符串按顺序转换为数组,元素为单词。
然后让所有事情都相同。
下一部分将取决于您的数据。您可以从较长的字符串中删除短字,您可以删除某些关键字,您可以检查数组中的大多数长字是否匹配...根据您选择的内容,这可能是计算密集型的。
我确信现有的工具也适用于此类事情。取决于你的目标是什么。
答案 3 :(得分:2)
您可以尝试使用Fuzzy String Matching
。您可以使用this Python Library。
它在内部使用Levenshtein距离(由@ user3080953建议)来计算两个单词/短语之间的相似性。
fuzz.ratio("hello", "hello")
Out: 100
fuzz.ratio("L & L AIR CONDITIONING", "L & L AIR CONDITIONING Service")
Out: 85
您可以设置一个阈值,在此阈值之上,您可以将两个单词/短语视为相似。