首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >替代Levenshtein和Trigram

替代Levenshtein和Trigram
EN

Stack Overflow用户
提问于 2013-11-23 13:28:29
回答 6查看 10.7K关注 0票数 20

假设我的数据库中有以下两个字符串:

代码语言:javascript
运行
复制
(1) 'Levi Watkins Learning Center - Alabama State University'
(2) 'ETH Library'

我的软件接收来自数据源的免费文本输入,它应该将这些免费文本与数据库中预定义的字符串(上面的字符串)相匹配。

例如,如果软件获得字符串'Alabama University',,它应该认识到这与(1)更相似,而不是(2)

起初,我想使用一个著名的字符串度量,比如Levenshtein或Trigram,但是这会导致不必要的结果,正如您在这里看到的:

http://fuzzy-string.com/Compare/Transform.aspx?r=Levi+Watkins+Learning+Center+-+Alabama+State+University&q=Alabama+University

http://fuzzy-string.com/Compare/Transform.aspx?r=ETH+Library&q=Alabama+University

代码语言:javascript
运行
复制
Difference to (1): 37
Difference to (2): 14

(2)获胜是因为它比(1)短得多,尽管(1)包含搜索字符串的单词(AlabamaUniversity)。

我也尝试了Trigram(使用Javascript库fuzzySet),但是我在那里得到了类似的结果。

是否有一个字符串度量可以识别搜索字符串与(1)**?** 的相似性

EN

回答 6

Stack Overflow用户

发布于 2016-03-24 05:21:26

您可以尝试移动器的距离https://github.com/mkusner/wmd这个词。该算法的一个突出优点是,它在计算文档中单词之间的差异时,融合了隐含的含义。这篇论文可以找到这里

票数 6
EN

Stack Overflow用户

发布于 2014-01-10 14:38:00

你应该改变你的方法:

levenshtein距离很好地计算单位的相似性,无论它们是“字符”还是“单词”。

从概念上讲,你认为阿拉巴马州和大学(2字)是两个单位,你想要计算出levenshtein距离应该表示阿拉巴马州和大学之间的单词之间的距离,这应该是1。

但是,您正在尝试应用levenshtein算法,该算法是为一个字内的字符实现的。这个实现只适用于匹配单个单词,而不是句子。

更好的是,您应该实现自己的levenshtein算法(使用better ),在顶部和每个匹配中,使用levenshtein来匹配每个单词。

您的(1)结果应该是距离1与该算法的匹配,而对于(2)则不匹配。

票数 4
EN

Stack Overflow用户

发布于 2018-06-18 15:06:29

我想答案不再是必需的了,但我喜欢这个问题,它让我思考如何结合RegEx + Levenshtein字符串度量的优点,但对距离的依赖程度较低。

到目前为止,我已经提出了一个解析器,它遵循以下前提和逻辑:

  • 它使用Python3和正则模 (OP没有提到任何语言/模块需求)
  • 搜索的任何needle都将从标点符号中删除。
  • 每个haystack都会去掉标点符号--所以N.A.S.A将成为NASA --就像在needle中,如果它最初是N.A.S.A.的话--我知道这在相当多的场景中是有问题的,但考虑到前提条件,我想不出一个更好的解决方案。
  • needle中每一个不超过3个字符的单词都会被删除(不需要is,on,at,no等)。
  • 匹配不区分大小写。
  • needle将被拆分为包含n项的wordgroupn是在dict 0 < k <= l中定义的,其中k是dict键。
  • wordgroup中的每个单词必须相互跟随,在它们之间有最大的n单词距离。
  • 每个单词,取决于它的n长度,可以有一个不同的允许错误阈值:总共可以指定e恐怖,可以指定substitions、inserts和d电子,也可以用一个丁字控制0 < k <= n的键。
  • 前面提到的dict包含键/lambda对,这对于它们的最后/第一项进行计算非常有用。

在线演示

contextual_fuzzy_matcher.py:

代码语言:javascript
运行
复制
from collections import OrderedDict
import regex


class ContextualFuzzyMatcher(object):
    maximum_word_distance = 2
    word_distance = r"\s(?:[\w]+\s){{0,{}}}".format(maximum_word_distance)
    punctuation = regex.compile(r"[\u2000-\u206F\u2E00-\u2E7F\\'!\"#$%&\(\)\*\+,\-\.\/:;<=>\?@\[\]\^_`\{\|\}~]")
    groups = OrderedDict((
        (0, lambda l: l),
        (4, lambda l: 3),
        (8, lambda l: 6),
        (10, lambda l: l // 0.75),
    ))
    tolerances = OrderedDict((
        (0, {
            'e': lambda l: 0,
            's': lambda l: 0,
            'i': lambda l: 0,
            'd': lambda l: 0,
        }),
        (3, {
            'e': lambda l: 1,
            's': lambda l: 1,
            'i': lambda l: 1,
            'd': lambda l: 1,
        }),
        (6, {
            'e': lambda l: 2,
            's': lambda l: 1,
            'i': lambda l: 1,
            'd': lambda l: 1,
        }),
        (9, {
            'e': lambda l: 3,
            's': lambda l: 2,
            'i': lambda l: 2,
            'd': lambda l: 2,
        }),
        (12, {
            'e': lambda l: l // 4,
            's': lambda l: l // 6,
            'i': lambda l: l // 6,
            'd': lambda l: l // 6,
        }),
    ))

    def __init__(self, needle):
        self.sentence = needle
        self.words = self.sentence_to_words(sentence)
        self.words_len = len(self.words)
        self.group_size = self.get_group_size()
        self.word_groups = self.get_word_groups()
        self.regexp = self.get_regexp()

    def sentence_to_words(self, sentence):
        sentence = regex.sub(self.punctuation, "", sentence)
        sentence = regex.sub(" +", " ", sentence)
        return [word for word in sentence.split(' ') if len(word) > 2]

    def get_group_size(self):
        return list(value for key, value in self.groups.items() if self.words_len >= key)[-1](self.words_len)

    def get_word_groups(self):
        return [self.words[i:i + self.group_size] for i in range(self.words_len - self.group_size + 1)]

    def get_tolerance(self, word_len):
        return list(value for key, value in self.tolerances.items() if word_len >= key)[-1]

    def get_regexp(self):
        combinations = []
        for word_group in self.word_groups:
            distants = []
            for word in word_group:
                word_len = len(word)
                tolerance = self.get_tolerance(word_len)
                distants.append(r"({}){{e<={},s<={},i<={},d<={}}}".format(
                    word,
                    tolerance['e'](word_len),
                    tolerance['s'](word_len),
                    tolerance['i'](word_len),
                    tolerance['d'](word_len),
                ))
            combinations.append(
                self.word_distance.join(distants)
            )
        return regex.compile(
            r"|".join(combinations),
            regex.MULTILINE | regex.IGNORECASE
        )

    def findall(self, haystack):
        return self.regexp.findall(haystack)

main.py:

代码语言:javascript
运行
复制
test_sentences = [
    'Levi Watkins Learning Center - Alabama State University',
    'ETH Library'
]
test_texts = [
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sapien eget mi proin sed libero enim sed. Nec tincidunt praesent semper feugiat nibh sed pulvinar. Habitasse platea dictumst quisque sagittis. Tortor condimentum lacinia quis vel eros donec ac odio. Platea dictumst vestibulum rhoncus est pellentesque elit ullamcorper dignissim. Ultricies tristique nulla aliquet enim tortor at. Mi proin sed libero enim sed faucibus. Fames ac turpis egestas integer eget aliquet nibh. Potenti nullam ac tortor vitae purus faucibus ornare suspendisse. Cras semper auctor neque vitae tempus quam pellentesque nec. Quam lacus suspendisse faucibus interdum posuere. Neque laoreet suspendisse interdum consectetur libero id faucibus nisl tincidunt. Viverra tellus in hac habitasse. Nibh nisl condimentum id venenatis a condimentum vitae. Tincidunt dui ut ornare lectus."
    "Mattis aliquam faucibus purus in massa tempor nec feugiat nisl. Amet consectetur adipiscing elit ut aliquam purus. Turpis massa tincidunt dui ut ornare. Suscipit tellus mauris a diam maecenas sed enim ut sem. Id consectetur purus ut faucibus pulvinar elementum. Est velit egestas dui id. Felis imperdiet proin fermentum leo. Faucibus nisl tincidunt eget nullam non nisi est sit. Elit pellentesque habitant morbi tristique. Nisi lacus sed viverra tellus. Morbi tristique senectus et netus et malesuada fames. Id diam vel quam elementum pulvinar. Id nibh tortor id aliquet lectus. Sem integer vitae justo eget magna. Quisque sagittis purus sit amet volutpat consequat. Auctor elit sed vulputate mi sit amet. Venenatis lectus magna fringilla urna porttitor rhoncus dolor purus. Adipiscing diam donec adipiscing tristique risus nec feugiat in fermentum. Bibendum est ultricies integer quis."
    "Interdum posuere lorem ipsum dolor sit. Convallis convallis tellus id interdum velit. Sollicitudin aliquam ultrices sagittis orci a scelerisque purus. Vel quam elementum pulvinar etiam. Adipiscing bibendum est ultricies integer quis. Tellus molestie nunc non blandit. Sit amet porttitor eget dolor morbi non arcu. Scelerisque purus semper eget duis at tellus. Diam maecenas sed enim ut sem viverra. Vulputate odio ut enim blandit volutpat maecenas. Faucibus purus in massa tempor nec. Bibendum ut tristique et egestas quis ipsum suspendisse. Ut aliquam purus sit amet luctus venenatis lectus magna. Ac placerat vestibulum lectus mauris ultrices eros in cursus turpis. Feugiat pretium nibh ipsum consequat nisl vel pretium. Elit pellentesque habitant morbi tristique senectus et.",
    "Found at ETH's own Library", # ' will be a problem - it adds one extra deletion
    "State University of Alabama has a learning center called Levi Watkins",
    "The ETH library is not to be confused with Alabama State university's Levi Watkins Learning center",
    "ETH Library",
    "Alabma State Unversity",
    "Levi Wtkins Learning"
]


for test_sentence in test_sentences:
    parser = ContextualFuzzyMatcher(test_sentence)
    for test_text in test_texts:
        for match in parser.findall(test_text):
            print(match)

返回:

代码语言:javascript
运行
复制
('', '', '', '', '', '', '', '', '', '', '', '', ' Alabama', 'State', 'university')
(' Levi', 'Watkins', 'Learning', '', '', '', '', '', '', '', '', '', '', '', '')
('', '', '', '', '', '', '', '', '', '', '', '', 'Alabma', 'State', 'Unversity')
('Levi', 'Wtkins', 'Learning', '', '', '', '', '', '', '', '', '', '', '', '')
(' ETH', 'library')
('ETH', 'Library')

我完全意识到,这离完美的解决方案还有很远的距离,而且我的例子很少,也没有真正的代表性--但也许通过调整配置和进行大量的现实世界测试,它可能能够涵盖相当多的情况,而不会产生太多的假阳性。而且,由于它是基于类的,它可以根据不同的来源被不同的继承和配置--也许在科学文本中,最大字距1就足够了,在报纸文章中可能需要3,等等。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20162894

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档