【问题标题】:repeated phrases in the text Python文本 Python 中的重复短语
【发布时间】:2010-12-24 14:11:45
【问题描述】:

我有一个问题,我不知道如何解决它。请给点建议。

我有一个文本。大,大的文字。任务是找到文本中所有长度为3(包含三个单词)的重复短语。

【问题讨论】:

  • 具体来说,很难遇到大到明显算法不起作用的文本(列出所有三词短语并计算它们)。

标签: python text repeat


【解决方案1】:

在我看来,你有两个问题。

首先是想出一种有效的方法来规范化输入。你说你想在输入中找到所有的三词短语,但是什么是短语?例如,the black dogThe black, dog? 是同一个短语吗?

正如 marcog 建议的那样,一种方法是使用类似re.findall 的东西。但这非常低效:它遍历您的整个输入并将单词复制到一个列表中,然后您必须处理该列表。如果你的输入文本很长,那会浪费时间和空间。

更好的方法是将输入视为流,并构建一个一次提取一个单词的生成器。这是一个示例,它使用空格作为单词之间的分隔符,然后从单词中去除非字母字符并将它们转换为小写:

>>> def words(text):
       pattern = re.compile(r"[^\s]+")
       non_alpha = re.compile(r"[^a-z]", re.IGNORECASE)
       for match in pattern.finditer(text):
           nxt = non_alpha.sub("", match.group()).lower()
           if nxt:  # skip blank, non-alpha words
               yield nxt


>>> text
"O'er the bright blue sea, for Sir Joseph Porter K.C.B."
>>> list(words(text))
['oer', 'the', 'bright', 'blue', 'sea', 'for', 'sir', 'joseph', 'porter', 'kcb']

第二个问题是将规范化的单词分组为三个单词的短语。同样,这里是生成器可以高效执行的地方:

>>> def phrases(words):
        phrase = []
        for word in words:
            phrase.append(word)
            if len(phrase) > 3:
                phrase.remove(phrase[0])
            if len(phrase) == 3:
                yield tuple(phrase)

>>> list(phrases(words(text)))
[('oer', 'the', 'bright'), ('the', 'bright', 'blue'), ('bright', 'blue', 'sea'), ('blue', 'sea', 'for'), ('sea', 'for', 'sir'), ('for', 'sir', 'joseph'), ('sir', 'joseph', 'porter'), ('joseph', 'porter', 'kcb')]

几乎可以肯定,该功能可能有一个更简单的版本,但这个功能很有效,而且不难理解。

值得注意的是,将生成器链接在一起只会遍历列表一次,并且不会在内存中构建任何大型临时数据结构。您可以使用结果构建一个以短语为键的defaultdict

>>> import collections
>>> counts = collections.defaultdict(int)
>>> for phrase in phrases(words(text)):
        counts[phrase] += 1

这会在计算短语时跳过text。完成后,查找字典中值大于 1 的每个条目。

【讨论】:

【解决方案2】:

最粗略的方法是读取字符串中的文本。执行 string.split() 并获取列表中的单个单词。然后,您可以按三个单词对列表进行切片,并使用 collections.defaultdict(int) 来保持计数。

d = collections.defaultdict(int)

d[短语]+=1

正如我所说,它非常粗糙。但肯定会让你开始

【讨论】:

  • 我在发布这个问题之前就开始这样做了,但这很粗糙。问题是文本包含大量的 sumbols,例如 ! ? : " . ( ) 等。我应该为每个 sumbol 写 string.split( )吗?
  • @user re.findall(r"[\w']+", "Hello, world!") 会是一个更好的起点。
  • @user 是的,标点符号很快就会变得复杂。如果您在句号上拆分,那么缩写将拆分为多个字母。引号(可能是撇号)呢?所以我认为你必须做出一些假设。要做到这一点,需要一个分类系统(就像 NLTK 一样),但这会相对较慢,并且可能不会带来足够显着的改进。
  • 查看以下链接删除标点符号和符号stackoverflow.com/questions/265960/…。但是,如之前的评论所述,它有其自身的缺点。然后,您可能应该按照@winweed 的建议查看 NLTK。
【解决方案3】:

我建议查看 NLTK 工具包。这是开源的,旨在用于自然语言教学。除了更高级别的 NLP 函数,它还有很多标记类型的函数和集合。

【讨论】:

    【解决方案4】:

    这是一个大约 O(n) 的解决方案,它应该适用于相当大的输入文本。如果速度太慢,您可能需要考虑使用专为文本处理而设计的 Perl 或纯性能的 C++。

    >>> s = 'The quick brown fox jumps over the lazy dog'
    >>> words = string.lower(s).split()
    >>> phrases = collections.defaultdict(int)
    >>> for a, b, c in zip(words[:-3], words[1:-2], words[2:]):
    ...     phrases[(a, b, c)] += 1
    ... 
    >>> phrases
    defaultdict(<type 'int'>, {('over', 'the', 'lazy'): 1, ('quick', 'brown', 'fox'): 1, ('the', '
    quick', 'brown'): 1, ('jumps', 'over', 'the'): 1, ('brown', 'fox', 'jumps'): 1, ('fox', 'jumps
    ', 'over'): 1})
    >>> [phrase for phrase, count in phrases.iteritems() if count > 1]
    >>> []
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-10
      • 1970-01-01
      • 2015-06-27
      • 1970-01-01
      • 1970-01-01
      • 2023-03-07
      • 2021-11-20
      相关资源
      最近更新 更多