Python中的字符串相似度

9

我正在尝试在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 的形式存储,尽管我认为这可能会增加一些复杂性,但我可以通过重构字符串来解决。


1
为什么不直接去掉所有标点符号然后进行比较呢?请参考https://dev59.com/1nVC5IYBdhLWcg3whRgw - Akshat Mahajan
@Liongold 这个想法在我打问题的时候就出现过了,谢谢,我会看一下链接的。 - Midavalo
那么,The Title: The SubtitleTitle, The: Subtitle, The 也应该被视为相等的吗? - Iron Fist
@IronFist 是的,虽然我认为处理 The 可能比直接比较它们更好。 - Midavalo
尝试使用FuzzyWuzzy库。 - whackamadoodle3000
可能是查找两个字符串之间相似度量的方法的重复问题。 - Trilarion
8个回答

17

你正在尝试做的事情已经在jellyfish包中得到了很好的实现。

>>> import jellyfish
>>> jellyfish.levenshtein_distance('jellyfish', 'smellyfish')
2

谢谢DevShark,我认为这个答案加上其他答案的部分将使我接近我所希望的地方。 - Midavalo
太好了,很高兴能帮助到你。 - DevShark

12

标准库的difflib模块提供了一个函数get_close_matches,用于进行模糊字符串匹配。

>>> import difflib
>>> difflib.get_close_matches('python', ['snakes', 'thon.py', 'pythin'])
['pythin', 'thon.py']  # ordered by similarity score

difflib使用Ratcliff/Obershelp https://xlinux.nist.gov/dads/HTML/ratcliffObershelp.html,但在某些情况下可能不如Levenshtein好。 - wordsforthewise

4
您可以使用in关键字。它不是相似性比较,而是实现您想要的功能:
s = "Title: Subtitle"

if "Title" in s or "Subtitle" in s:
    match = 'True'
else:
    match = 'False'

2

尝试替换字符,然后检查它们是否相等:

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
>>> 

2
如果唯一的障碍是标点符号,那么问题就很简单:只需丢弃非单词字符并比较剩余的单词列表即可。
s1 = 'Title - Subtitle'
toks1 = re.split(r"^\W+", s1)  # keep just the words
toks1 = [ w.lower() for w in toks1 ]

我加入了小写字母,因为这也可能不同。对每个输入应用相同的处理并比较列表。
但正如您所指出的,还可能存在其他差异。如果您的数据确实包含标题(书籍、电影、科学文章),则可以首先删除冠词和常见连词(所谓的“停用词”),就像图书馆一样。例如,“文章的标题”被削减为["title", "article"]。为了处理单词顺序中可能存在的其他差异,您可以使用信息检索中常见的所谓“词袋”方法。将标记列表转换为集合,或者转换为单词计数的字典,以处理某些单词出现多次的情况。以下是一个示例,使用单词计数和nltk的“停用词”列表作为过滤器。
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")

如果有更多的变化,那就无限制了。近似文本匹配是一个活跃研究领域,应用于信息检索、抄袭检测、遗传学等领域。您可以检查一个标题是否是另一个标题的子集(也许有人省略了副标题)。您可以尝试通过“编辑距离”进行匹配(例如,其他答案提到的“Levenshtein距离”),将其应用到字母或整个单词上。您可以尝试使用TF-IDF分数等信息检索算法。这些只是您可以尝试的一些方法,因此请寻找最简单的解决方案,以便为您完成工作。Google是您的好朋友。

非常有用的答案,谢谢。显然,这比我最初想象的要复杂得多。 - Midavalo
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - alexis

1
我是一名Ruby程序员,对Python没有经验,但在Ruby中,这样的问题可以通过使用Levensthein gem来快速解决。它计算您需要进行的编辑次数以使字符串达到相同的状态。
我看到Python也有一个类似的解决方案,请查看https://pypi.python.org/pypi/python-Levenshtein

1
这应该可以工作。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'

0

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

谷歌搜索“正则表达式”,你会找到更好的实现这种方法的方式。 - alexis
@alexis...当然我知道re解决方案,但我只是试图把它作为最后的手段留下来,以免复杂化事情...无论如何...在我的意图中,我正在使用re的另一种替代方式,我已经发布了它。 - Iron Fist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接