2013-10-01 84 views
1

我有一個6M行數據庫(sqlite)包含4個字段:id(整數)|標題(文本)|文本(文本)|標記(文本)。python計算大文件中的單詞

現在我需要計算標題中出現的每個單詞的出現次數,並將其導入到其他表格中,例如word | count和tag | word | count。

我的代碼看起來像這樣在Python 2.7:

from nltk.tokenize import wordpunct_tokenize 
from collections import Counter 
import sqlite3 

word_count = Counter() 
pair_count = Counter() 

conn = sqlite3.connect('database') 
c = conn.cursor() 

for query in c.execute('SELECT Tags, Title FROM data'): 
    tags = query[0].strip().split() 
     title = wordpunct_tokenize(query[1]) 
     for word in title: 
      word_count[word] += 1 
      for tag in tags: 
       pair_count[(tag, word)] += 1 
... 

的問題是,在櫃檯去如此之大,我在1M行有一個內存錯誤。我試圖每隔100K行重新初始化計數器並將計數添加到db文件,但這種方法似乎很慢,可能是由於標記字對的數量龐大。

... 
for query in c.execute('SELECT Tags, Title FROM data'): 
    i += 1 
    if i % 100000 == 0: 
     conn1 = sqlite3.connect('counts.db') 
     c1 = conn1.cursor() 

     # update word count 
     for word in word_count: 
      c1.execute('SELECT Count FROM word_count WHERE Word=?', (word,)) 
      count = c1.fetchone() 
      # add to existing count and update 
      if count: 
       count = word_count[word] + count[0] 
       c1.execute('UPDATE word_count SET Count=? WHERE Word=?', (count, word)) 
      # insert new row 
      else: 
       c1.execute('INSERT INTO title_word_count VALUES (?,?)', (word, word_count[word])) 

     # update pair count     
     for pair in pair_count: 
      c1.execute('SELECT Count FROM pair_count WHERE Tag=? AND Word=?', pair) 
      count = c1.fetchone() 
      if count: 
       count = pair_count[pair] + count[0] 
       c1.execute('UPDATE pair_count SET Count=? WHERE Tag=? AND Word=?', (count, pair[0], pair[1])) 
      else: 
       c1.execute('INSERT INTO pair_count VALUES (?,?,?)', (pair[0], pair[1], pair_count[pair])) 
     conn1.commit() 
     conn1.close() 

     # reinitiate counters 
     word_count = Counter() 
     pair_count = Counter() 
... 

有沒有辦法可以解決這個問題,而無需訪問多臺機器?此外,任何建議的代碼將不勝感激!


編輯:

我試圖指數counts.db和更新每個批次,但它仍然是太慢了 - 它花了10個小時,處理的每20萬行7個批次。

我按照我最初的想法結束了。但不是更新每100K行的計數,我只是將它們插入表subcounts儘管可能重複Tag, Word對。

Then INSERT INTO pair_count SELECT Tag, Word, SUM(Count) FROM subcounts GROUP BY Tag, Word;給了我最後的結果。我花了大約3小時。

我不小心丟棄了臨時表,我收到了@abernert建議,但我認爲這是可行的。

感謝@Steve和@abernert爲您的建議!

+0

只要檢查一下顯而易見的事情:你在'counts.db'中的表上有正確的索引嗎? –

+0

@SteveJessop我不確定你的意思。 'counts.db'存儲記錄標籤計數,字數和配對計數的表格。 –

+0

好的,在這種情況下,您可能需要研究「SQL索引」的主題。如果您沒有它們,那麼每個查詢都可能掃描整個表格,並且您每對數據執行兩個查詢。 –

回答

3

如果您按照(標記,單詞)的順序排列了行,那麼您將獲得一對計數的所有更新,然後獲得下一個的所有更新,依此類推。

不幸的是,因爲你沒有正確地規範你的數據,你不能得到這些。

如果您不知道最後一句的含義是什麼,則需要閱讀數據庫規範化。 Third normal form維基百科看起來是一個很好的開始。

如果你不能修復你的數據模型,我們可以建立一個臨時表來解決這個問題:

c.execute('DROP TABLE IF EXISTS _data') 
c.execute('CREATE TABLE _data (Tag, Word)') 
for query in c.execute('SELECT Tags, Title FROM data'): 
    tags = query[0].strip().split() 
    words = wordpunct_tokenize(query[1]) 
    c.executemany('INSERT INTO _data (Tag, Word) VALUES(?, ?)', 
        itertools.product(tags, words)) 
c.commit() 

你實際上並不需要拆出兩列,只是哪一個較大。但是這更清潔,除非你真的需要節省磁盤空間。

無論如何,現在你可以ORDER BY要麼Tag, WordWord, Tag,這取決於哪一個更大,你並不需要保存tag_count值的整體轉換身邊,只是你目前的工作之一。你會得到一個值的所有行,然後是下一個的所有行,依此類推。

這也意味着,與GROUP BY,你可以讓sqlite3做你的計數。

這也意味着你不需要在Python中迭代;你可以讓sqlite3也這樣做:

c.execute('''INSERT INTO pair_count 
      SELECT Tag, Word, COUNT(*) FROM _data GROUP BY Tag, Word''') 
+0

這在第一次正常工作。我得到了一個包含所有'Tag,Word'對的100 GB數據庫,並試圖讓sqlite進行計數。該查詢運行了一段時間,然後拋出一個錯誤「**數據庫或磁盤已滿**」。我在磁盤上至少還有100 GB,計數不應該超過此值。 –

+0

你指的是哪個「this」,迭代ORDER BY,所以你不需要一次在內存中保存多個標記,遍歷一個GROUP BY,這樣你就可以得到每個標記每個字的行數或者反之亦然)而不是每一行,或者在SQL中完成整個事情?前兩項會讓你看到你是否獲得了90%或5%,這在這裏可能會有所幫助。 – abarnert

+0

另外,如果您使用純SQL版本,是否可以運行SELECT語句並遍歷結果以查看在查詢本身中還是在獲取結果中是否出現錯誤?你能檢查頁面大小嗎?你會得到IIRC,1G頁面; 512字節的頁面對於表格和任何臨時表格都是512GB。 ('PRAGMA page_size'將顯示答案,'PRAGMA page_size = 16384'將在創建任何表之前運行它,並且在大多數情況下,'PRAGMA page_size = 16384; VACUUM'將重新分頁現有的db 。您可能還想查看'PRAGMA page_count'。) – abarnert

相關問題