Python计算大文件中的单词

时间:2013-10-01 22:49:43

标签: python sql sqlite memory python-2.7

我有一个包含4个字段的6M行数据库(sqlite):id(整数)| title(文本)| text(text)| tags(text)。

现在我需要计算标题中出现的每个单词的出现次数,并导入其他表格,如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小时才能处理7批20万行。

我最终遵循了我最初的想法。但是,我不是每100K行更新一次计数,而是将它们插入到表subcounts中,尽管可能存在重复的Tag, Word对。

然后INSERT INTO pair_count SELECT Tag, Word, SUM(Count) FROM subcounts GROUP BY Tag, Word;给了我最终结果。我花了大约3个小时。

我不小心放弃了@abernert建议后的临时表,但我认为这是可行的。

感谢@Steve和@abernert的建议!

1 个答案:

答案 0 :(得分: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''')