如何有效地从连续字符串中提取文字单词?

时间:2012-07-20 09:39:50

标签: python algorithm extract text-extraction

  

可能重复:
  How to split text without spaces into list of words?

人们的评论中有大量的文本信息是从html中解析出来的,但是它们中没有分隔字符。例如:thumbgreenappleactiveassignmentweeklymetaphor。显然,字符串中有“拇指”,“绿色”,“苹果”等。我还有一个大词典来查询这个词是否合理。 那么,提取这些词的最快方法是什么?

2 个答案:

答案 0 :(得分:6)

我不太确定一个天真的算法能很好地满足你的目的,正如eumiro所指出的那样,所以我将描述一个稍微复杂的算法。

想法

最好的方法是建模输出的分布。一个好的第一近似是假设所有单词都是独立分布的。然后你只需要知道所有单词的相对频率。可以合理地假设它们遵循Zipf定律,即单词列表中具有等级 n 的单词的概率大概为1 /( n log N < / em>)其中 N 是字典中的单词数。

修复模型后,可以使用动态编程来推断空间的位置。最可能的句子是最大化每个单词的概率乘积的句子,并且通过动态编程很容易计算它。我们不是直接使用概率,而是使用定义为概率倒数的对数的成本来避免溢出。

代码

import math

# Build a cost dictionary, assuming Zipf's law and cost = -math.log(probability).
words = open("words-by-frequency.txt").read().split()
wordcost = dict((k,math.log((i+1)*math.log(len(words)))) for i,k in enumerate(words))
maxword = max(len(x) for x in words)

def infer_spaces(s):
    """Uses dynamic programming to infer the location of spaces in a string
    without spaces."""

    # Find the best match for the i first characters, assuming cost has
    # been built for the i-1 first characters.
    # Returns a pair (match_cost, match_length).
    def best_match(i):
        candidates = enumerate(reversed(cost[max(0, i-maxword):i]))
        return min((c + wordcost.get(s[i-k-1:i], 9e999), k+1) for k,c in candidates)

    # Build the cost array.
    cost = [0]
    for i in range(1,len(s)+1):
        c,k = best_match(i)
        cost.append(c)

    # Backtrack to recover the minimal-cost string.
    out = []
    i = len(s)
    while i>0:
        c,k = best_match(i)
        assert c == cost[i]
        out.append(s[i-k:i])
        i -= k

    return " ".join(reversed(out))

可以与

一起使用
s = 'thumbgreenappleactiveassignmentweeklymetaphor'
print(infer_spaces(s))

实施例

我正在使用维基百科的一小部分this quick-and-dirty 125k-word dictionary I put together

  

之前: thumbgreenappleactiveassignmentweeklymetaphor。
  之后:拇指青苹果主动分配每周比喻。

  

之前:以及来自其他人的各种评论的文本信息   odelimitedcharactersinthemforexamplethumbgreenappleactiveassignmentweeklymetapho   rapparentlytherearethumbgreenappleetcinthestringialsohavealargedictionarytoquery   whetherthewordisreasonablesowhatsthefastestwayofextractionthxalot。

     

之后:有很多人民评论的文字信息,这些信息都是从html中解析出来的,但其中没有分隔符,例如拇指青苹果主动分配每周隐喻显然有拇指青苹果等在字符串中我还有一个大字典来查询这个单词是否合理,所以最快的提取方法是什么。

  

之前: itwasadarkandstormynighttherainfellintorrentsexceptatoccasionalintervalswhenitwascheckbyaviolentgustofwindwhitsptptupthestreetreetitontontonsnelnelneltlingalongthetoptopsandfiercelyratanttantyflameoftlampsthattrutledtaindtheddarkhedarkness。      

之后:这是一个黑暗而暴风雨的夜晚,雨水在洪流中肆虐,除非是偶尔的间隔时间,一阵猛烈的风吹过街道,因为它在伦敦,我们的场景沿着屋顶嘎嘎作响,激烈地搅动着与黑暗作斗争的灯火焰。

正如您所看到的,它基本上是无瑕疵的。最重要的部分是确保你的单词列表被训练成类似于你实际会遇到的语料库,否则结果会非常糟糕。


优化

实现消耗了线性的时间和内存,因此效率相当高。如果您需要进一步加速,可以从单词列表构建后缀树,以减少候选集的大小。

如果需要处理一个非常大的连续字符串,拆分字符串以避免过多的内存使用是合理的。例如,您可以处理10000个字符的块中的文本以及两侧的1000个字符的边距,以避免边界效应。这将使内存使用量降至最低,几乎肯定不会影响质量。

答案 1 :(得分:4)

“显然”对人类有益,而不是对计算机......

words = set(possible words)
s = 'thumbgreenappleactiveassignmentweeklymetaphor'
for i in xrange(len(s) - 1):
    for j in xrange(1, len(s) - i):
        if s[i:i+j] in words:
            print s[i:i+j]

对于/usr/share/dict/wordsfor j in xrange(3, len(s) - i):中的可能单词(最小单词长度为3),它会找到:

thumb
hum
green
nap
apple
plea
lea
act
active
ass
assign
assignment
sign
men
twee
wee
week
weekly
met
eta
tap