Python 中的字符串相似度
String similarity in Python
我正在尝试比较 Python 中的字符串。我的字符串包含可以采用多种不同方式构建的标题:
'Title'
'Title: Subtitle'
'Title - Subtitle'
'Title, Subtitle'
'Title Subtitle'
是否可以在Python中做相似度比较,从而确定match('Title: Subtitle', 'Title - Subtitle') = True
? (或者无论如何构造)
基本上,即使拆分不同,我也在尝试确定它们是否是同一个标题。
if 'Title: Subtitle' == 'Title - Subtitle':
match = 'True'
else:
match = 'False'
还有一些可能存储为 The Title: The Subtitle
或 Title, The: Subtitle, The
,尽管我认为这可能会增加一些复杂性,但我可以通过重建字符串来解决。
我是一名 Ruby 程序员,所以没有 Python 方面的经验,但是在 Ruby 中,使用 gem Levensthein 可以很快解决这样的问题。它计算您需要对一个字符串进行多少次编辑才能得到相同的字符串。
我看到还有一个 Python 等价物,所以看看 https://pypi.python.org/pypi/python-Levenshtein
您可以使用 in
关键字。这不是相似性比较,而是做你想做的:
s = "Title: Subtitle"
if "Title" in s or "Subtitle" in s:
match = 'True'
else:
match = 'False'
尝试替换字符,然后检查是否相等:
def match(str1, str2):
str1 = str1.replace(' -', '').replace(',', '').replace(':', '')
str2 = str2.replace(' -', '').replace(',', '').replace(':', '')
return str1 == str2
>>> match('Title: Subtitle', 'Title - Subtitle')
True
>>> match('Title: Subtitle', 'Title, Subtitle')
True
>>>
您正在尝试做的事情已经在 jellyfish 包中很好地实现了。
>>> import jellyfish
>>> jellyfish.levenshtein_distance('jellyfish', 'smellyfish')
2
这应该有效。 Python 翻译可以用来取出任何不同的字符。
titles = ['Title: Sub', 'Title Sub', 'Title - Sub']
s = ': -'
if titles[1].translate(None, s) == titles[2].translate(None, s):
match = 'True'
else
match = 'False'
fnmatch.fnmatch
虽然专为 Unix 文件名匹配而设计,但在这里也很方便,请考虑以下示例:
>>> from fnmatch import fnmatch
>>> l
['Title: Subtitle', 'Title - Subtitle', 'Title, Subtitle', 'Title Subtitle']
>>>
>>> all(fnmatch(x, 'Title*Subtitle') for x in l)
True
另一种方法,将检查它们是否都匹配 re
模式:
>>> import re
>>> l
['Title: Subtitle', 'Title - Subtitle', 'Title, Subtitle', 'Title Subtitle']
>>> all(re.search(r'^Title.*?Subtitle$', x) for x in l)
True
如果唯一的障碍是标点符号,问题就微不足道了:只需丢弃 non-word 个字符并比较剩余的单词列表。
s1 = 'Title - Subtitle'
toks1 = re.split(r"^\W+", s1) # keep just the words
toks1 = [ w.lower() for w in toks1 ]
我加入了小写字母,因为这也可能不同。将相同的应用到每个输入并比较列表。
但正如您所指出的,可能还有其他差异。如果您的数据确实包含标题(书籍、电影、科学文章),您可以像图书馆一样,先删除文章和常用连接词 (so-called "stopwords")。例如,"The title of the article" 被精简为 ["title", "article"]
。要处理其他可能的词序差异,您可以使用信息检索中常见的 so-called "bag of words" 方法。将标记列表转换为一个集合,或者在某些单词出现多次的情况下转换为单词计数字典。这是一个示例,使用字数统计和 nltk
的 "stopword" 列表作为过滤器。
import nltk
from collections import Counter
stopwords = set(nltk.corpus.stopwords.words("english"))
toks1 = [ t for t in toks1 if t not in stopwords ]
cnt1 = Counter(toks1)
cnt2 = Counter(toks2) # Another title string, processed the same way
if cnt1 == cnt2:
print("The two strings have exactly the same content words")
如果还有更多的变化,天空是极限。近似文本匹配是一个活跃的研究主题,在信息检索、剽窃检测、遗传学等方面都有应用。您可以检查一个标题是否是另一个标题的子集(也许有人遗漏了副标题)。您可以尝试通过 "edit distance" 进行匹配(例如,其他几个答案提到的 "Levenshtein distance" ),将其应用于字母或整个单词。你可以尝试像 TF-IDF score 这样的信息检索算法。这些只是您可以尝试的一些事情,因此请寻找最简单的解决方案来完成这项工作。 Google 是你的朋友。
标准库的 difflib
模块提供了一个函数 get_close_matches
进行模糊字符串匹配。
>>> import difflib
>>> difflib.get_close_matches('python', ['snakes', 'thon.py', 'pythin'])
['pythin', 'thon.py'] # ordered by similarity score
我正在尝试比较 Python 中的字符串。我的字符串包含可以采用多种不同方式构建的标题:
'Title'
'Title: Subtitle'
'Title - Subtitle'
'Title, Subtitle'
'Title Subtitle'
是否可以在Python中做相似度比较,从而确定match('Title: Subtitle', 'Title - Subtitle') = True
? (或者无论如何构造)
基本上,即使拆分不同,我也在尝试确定它们是否是同一个标题。
if 'Title: Subtitle' == 'Title - Subtitle':
match = 'True'
else:
match = 'False'
还有一些可能存储为 The Title: The Subtitle
或 Title, The: Subtitle, The
,尽管我认为这可能会增加一些复杂性,但我可以通过重建字符串来解决。
我是一名 Ruby 程序员,所以没有 Python 方面的经验,但是在 Ruby 中,使用 gem Levensthein 可以很快解决这样的问题。它计算您需要对一个字符串进行多少次编辑才能得到相同的字符串。
我看到还有一个 Python 等价物,所以看看 https://pypi.python.org/pypi/python-Levenshtein
您可以使用 in
关键字。这不是相似性比较,而是做你想做的:
s = "Title: Subtitle"
if "Title" in s or "Subtitle" in s:
match = 'True'
else:
match = 'False'
尝试替换字符,然后检查是否相等:
def match(str1, str2):
str1 = str1.replace(' -', '').replace(',', '').replace(':', '')
str2 = str2.replace(' -', '').replace(',', '').replace(':', '')
return str1 == str2
>>> match('Title: Subtitle', 'Title - Subtitle')
True
>>> match('Title: Subtitle', 'Title, Subtitle')
True
>>>
您正在尝试做的事情已经在 jellyfish 包中很好地实现了。
>>> import jellyfish
>>> jellyfish.levenshtein_distance('jellyfish', 'smellyfish')
2
这应该有效。 Python 翻译可以用来取出任何不同的字符。
titles = ['Title: Sub', 'Title Sub', 'Title - Sub']
s = ': -'
if titles[1].translate(None, s) == titles[2].translate(None, s):
match = 'True'
else
match = 'False'
fnmatch.fnmatch
虽然专为 Unix 文件名匹配而设计,但在这里也很方便,请考虑以下示例:
>>> from fnmatch import fnmatch
>>> l
['Title: Subtitle', 'Title - Subtitle', 'Title, Subtitle', 'Title Subtitle']
>>>
>>> all(fnmatch(x, 'Title*Subtitle') for x in l)
True
另一种方法,将检查它们是否都匹配 re
模式:
>>> import re
>>> l
['Title: Subtitle', 'Title - Subtitle', 'Title, Subtitle', 'Title Subtitle']
>>> all(re.search(r'^Title.*?Subtitle$', x) for x in l)
True
如果唯一的障碍是标点符号,问题就微不足道了:只需丢弃 non-word 个字符并比较剩余的单词列表。
s1 = 'Title - Subtitle'
toks1 = re.split(r"^\W+", s1) # keep just the words
toks1 = [ w.lower() for w in toks1 ]
我加入了小写字母,因为这也可能不同。将相同的应用到每个输入并比较列表。
但正如您所指出的,可能还有其他差异。如果您的数据确实包含标题(书籍、电影、科学文章),您可以像图书馆一样,先删除文章和常用连接词 (so-called "stopwords")。例如,"The title of the article" 被精简为 ["title", "article"]
。要处理其他可能的词序差异,您可以使用信息检索中常见的 so-called "bag of words" 方法。将标记列表转换为一个集合,或者在某些单词出现多次的情况下转换为单词计数字典。这是一个示例,使用字数统计和 nltk
的 "stopword" 列表作为过滤器。
import nltk
from collections import Counter
stopwords = set(nltk.corpus.stopwords.words("english"))
toks1 = [ t for t in toks1 if t not in stopwords ]
cnt1 = Counter(toks1)
cnt2 = Counter(toks2) # Another title string, processed the same way
if cnt1 == cnt2:
print("The two strings have exactly the same content words")
如果还有更多的变化,天空是极限。近似文本匹配是一个活跃的研究主题,在信息检索、剽窃检测、遗传学等方面都有应用。您可以检查一个标题是否是另一个标题的子集(也许有人遗漏了副标题)。您可以尝试通过 "edit distance" 进行匹配(例如,其他几个答案提到的 "Levenshtein distance" ),将其应用于字母或整个单词。你可以尝试像 TF-IDF score 这样的信息检索算法。这些只是您可以尝试的一些事情,因此请寻找最简单的解决方案来完成这项工作。 Google 是你的朋友。
标准库的 difflib
模块提供了一个函数 get_close_matches
进行模糊字符串匹配。
>>> import difflib
>>> difflib.get_close_matches('python', ['snakes', 'thon.py', 'pythin'])
['pythin', 'thon.py'] # ordered by similarity score