2010-05-04 87 views
3

我有一個巨大的文本文件的字符串緩衝區。我必須在字符串緩衝區中搜索給定的單詞/短語。什麼是有效的方式來做到這一點?在Python中一次遍歷字符串字

我嘗試使用重新模塊匹配。但是,因爲我有一個巨大的文本語料庫,我必須搜索。這需要大量時間。

鑑於詞和短語的字典。

我通過每一個文件重複,讀取到的字符串,搜索所有的單詞和短語在字典和增加計數在字典中如果發現鑰匙。我們認爲

一個小小的優化是排序的短語/詞的字典,以最低的最大字數。然後比較字符串緩衝區中的每個字開始位置並比較字的列表。如果找到一個詞,我們不尋求其他的詞組(因爲它匹配最長的短語,這正是我們想要的)

有一個人可以建議如何去一字一句的字符串緩衝區。 (逐字迭代字符串緩衝區)?

此外,有沒有可以在此做任何其他的優化?

data = str(file_content) 
for j in dictionary_entity.keys(): 
    cnt = data.count(j+" ") 
    if cnt != -1: 
     dictionary_entity[j] = dictionary_entity[j] + cnt 
f.close() 
+0

我有一個巨大的文本語料庫,並且我試圖獲取這個語料庫中200萬個詞組/單詞的出現次數。 – AlgoMan 2010-05-04 20:16:09

+0

什麼是語料庫? – dlamotte 2010-05-04 20:21:12

+0

你正在實施一個單詞/短語計數器還是什麼? – dlamotte 2010-05-04 20:22:24

回答

7

迭代字的字通過一個文件(從Project Gutenberg的綠野仙蹤,在我的情況),三種不同方式的內容:

from __future__ import with_statement 
import time 
import re 
from cStringIO import StringIO 

def word_iter_std(filename): 
    start = time.time() 
    with open(filename) as f: 
     for line in f: 
      for word in line.split(): 
       yield word 
    print 'iter_std took %0.6f seconds' % (time.time() - start) 

def word_iter_re(filename): 
    start = time.time() 
    with open(filename) as f: 
     txt = f.read() 
    for word in re.finditer('\w+', txt): 
     yield word 
    print 'iter_re took %0.6f seconds' % (time.time() - start) 

def word_iter_stringio(filename): 
    start = time.time() 
    with open(filename) as f: 
     io = StringIO(f.read()) 
    for line in io: 
     for word in line.split(): 
      yield word 
    print 'iter_io took %0.6f seconds' % (time.time() - start) 

woo = '/tmp/woo.txt' 

for word in word_iter_std(woo): pass 
for word in word_iter_re(woo): pass 
for word in word_iter_stringio(woo): pass 

結果造成:

% python /tmp/junk.py 
iter_std took 0.016321 seconds 
iter_re took 0.028345 seconds 
iter_io took 0.016230 seconds 
+0

+1徹底的答案。 – 2010-05-04 22:25:32

0

如果re模塊無法快速完成,您將難以更快地完成任務。無論哪種方式,你需要閱讀整個文件。你可能會考慮修正你的正則表達式(你可以提供一個嗎?)。也許是你想要完成的一些背景。

0

你可以嘗試做它周圍的其他方法...而不是處理文本語料庫200萬倍(每次一個字),過程只有一次。對於語料庫中的每個單詞,增加一個散列表或類似的詞來存儲該單詞的計數。在僞代碼一個簡單的例子:

word_counts = new hash<string,int> 
for each word in corpus: 
    if exists(word_counts[word]): 
    word_counts[word]++ 
    else: 
    word_counts[word] = 1 

您可能能夠通過單詞的完整列表提前初始化word_counts加快步伐,這不需要,如果語句...不知道。

+0

但是哈希中的字符串可能是多個單詞。因此,與每個單詞比較會讓我算「城市」和「黃金」,但不是「黃金之城」 – AlgoMan 2010-05-04 20:25:53

+0

@AlgoMan,你不能爲each_word_or_phrase做任何理由,並將它們都粘在字典中。 – mikerobi 2010-05-04 20:44:21

+0

@mikerobi我可以把這些短語放在字典中。但是語料庫是逐字搜索的,而不是一句一句的。我如何搜索整個語料庫短語並在單詞上增加並再次搜索短語。 – AlgoMan 2010-05-04 21:13:45

0

正如xyld所說,我不認爲你可以擊敗re模塊的速度,儘管如果你發佈你的正則表達式和可能的代碼,它會有所幫助。我可以添加的只是在優化之前嘗試分析。當你看到大部分加工過程時,你可能會感到非常驚訝。我使用hotshot來描述我的代碼,並且對它很滿意。你可以在這裏找到一個很好的Python入門介紹http://onlamp.com/pub/a/python/2005/12/15/profiling.html

0

如果使用re的性能不夠好,那麼您可能使用的是findall(),或者手動逐個查找匹配項。使用迭代器可能會讓它更快一點:

>>> for i in re.finditer(r'\w+', 'Hello, this is a sentence.'): 
...  print i.group(0) 
...  
Hello 
this 
is 
a 
sentence 
1

這聽起來像這類問題,其中一個trie將真正幫助。你應該使用某種壓縮的trie,比如Patricia/radix trie。只要你能夠適合你在單詞樹中查找的單詞/短語的整個詞典,這將大大減少時間複雜度。它的工作原理是,先取一個單詞的開頭,然後下降,直到找到最長匹配並增加該節點中的計數器爲止。這可能意味着如果部分匹配不能平移,則必須提升trie。然後,你將繼續下一個單詞的開始,然後重新執行。這個特性的優點是你可以通過查找整個字典來搜索整個字典(每個查找應該花費大約O(m),其中m是字典中單詞/短語的平均長度)。

如果你不能將整個字典放入一個trie中,那麼你可以將字典分成幾次嘗試(一個用於所有以al開頭的單詞/短語,例如一個用於mz),然後掃描整個語料庫爲每個trie。

+0

我有單詞列表,50MB文件。有200萬字/短語,我需要搜索。 – AlgoMan 2010-05-04 21:14:49

+0

我剛用一個非常簡單的patricia trie實現做了一個200萬隨機生成的平均長度爲22.5個字母的短語測試,我想起了一段時間,並且在我的機器上花費了684 MB。我還將隨機生成的短語保存到文本文件中,文件爲48 MB。這看起來並不算太糟,特別是考慮到我的實現不是非常有效的內存。 – 2010-05-04 22:05:40

0
#!/usr/bin/env python 
import re 

s = '' 
for i in xrange(0, 100000): 
    s = s + 'Hello, this is a sentence. ' 
    if i == 50000: 
     s = s + " my phrase " 

s = s + 'AARRGH' 

print len(s) 

itr = re.compile(r'(my phrase)|(\w+)').finditer(s) 
for w in itr: 
    if w.group(0) == 'AARRGH': 
     print 'Found AARRGH' 
    elif w.group(0) == "my phrase": 
     print 'Found "my phrase"' 

運行此,我們得到

$ time python itrword.py 
2700017 
Found "my phrase" 
Found AARRGH 

real 0m0.616s 
user 0m0.573s 
sys  0m0.033s 

但是,每個「短語」明確添加到正則表達式將採取收費上表現 - 上面是比只用慢50%「\ w + 「,通過我粗略的測量。

+0

但是,如果我想搜索一個短語? 如果w.group(0)=='這是一個': print「found」這是一個''' 我該如何做這項工作? – AlgoMan 2010-05-04 21:26:46

+0

@AlgoMan:我認爲中心問題是,'有人可以建議如何在字符串緩衝區中逐字地去。 (逐字逐字串緩衝區)?'鑑於此,您將不得不在「for w in itr:」循環內添加一些狀態機或類似的內容以逐詞匹配短語。否則,將需要一個比「\ w +」更復雜的正則表達式。 – 2010-05-04 21:57:51

0

你有沒有考慮過看Natural Language Toolkit。它包含許多用於處理文本語料庫的很好的功能,還有一個類似字典(有鍵)和列表式(片)的酷FreqDist類。