【问题标题】:Saving huge bigram dictionary to file using pickle使用pickle将巨大的二元字典保存到文件
【发布时间】:2011-01-07 16:11:42
【问题描述】:

我的一个朋友写了这个小程序。 textFile 大小为 1.2GB(价值 7 年的报纸)。 他成功地创建了字典,但无法使用 pickle 将其写入文件(程序挂起)。

import sys
import string
import cPickle as pickle

biGramDict = {}

textFile = open(str(sys.argv[1]), 'r')
biGramDictFile = open(str(sys.argv[2]), 'w')


for line in textFile:
   if (line.find('<s>')!=-1):
      old = None
      for line2 in textFile:
         if (line2.find('</s>')!=-1):
            break
         else:
            line2=line2.strip()
            if line2 not in string.punctuation:
               if old != None:
                  if old not in biGramDict:
                     biGramDict[old] = {}
                  if line2 not in biGramDict[old]:
                     biGramDict[old][line2] = 0
                  biGramDict[old][line2]+=1
               old=line2

textFile.close()

print "going to pickle..."    
pickle.dump(biGramDict, biGramDictFile,2)

print "pickle done. now load it..."

biGramDictFile.close()
biGramDictFile = open(str(sys.argv[2]), 'r')

newBiGramDict = pickle.load(biGramDictFile)

提前致谢。

编辑
对于任何有兴趣的人,我将简要解释这个程序的作用。 假设您的文件格式大致如下:

<s>
Hello
,
World
!
</s>
<s>
Hello
,
munde
!
</s>
<s>
World
domination
.
</s>
<s>
Total
World
domination
!
</s>
  • &lt;s&gt; 是句子分隔符。
  • 每行一个字。

生成一个 biGramDictionary 供以后使用。
像这样:

{
 "Hello": {"World": 1, "munde": 1}, 
 "World": {"domination": 2},
 "Total": {"World": 1},
}

希望这会有所帮助。现在策略改为使用 mysql,因为 sqlite 不起作用(可能是因为大小)

【问题讨论】:

  • 如果您要处理大文件,为什么不使用数据库呢?另外,我看到你在同一个文件上循环了 2 次,这可能是多余的并增加了处理成本。为什么不描述您对示例输入文件所做的事情?
  • ghostdog74,你看到 2 个 for 语句,但文件上只有一个循环 :) 迭代文件只是读取行(从实际位置),它不会寻找到开头文件。
  • 只需尝试sqlitedict(您的 Python 字典由磁盘上的 DB 支持,而不是 RAM)。

标签: python file dictionary pickle


【解决方案1】:

Pickle 仅用于编写完整的(小)对象。您的字典有点大,甚至无法存储在内存中,您最好使用数据库代替,这样您就可以一个一个地存储和检索条目,而不是一次全部存储和检索。

可以在 Python 中使用的一些良好且易于集成的单一文件数据库格式是 SQLiteDBM variants 之一。最后一个就像字典一样(即您可以读取和写入键/值对),但使用磁盘作为存储而不是 1.2 GB 的内存。

【讨论】:

  • Sqlite 是一个完全关系型数据库,而 Berkeley DB 不是,只是键/值。如果只是存储,我认为 Berkeley 是一个更好的选择,而如果你想进行一些查询并以更有条理的方式存储信息,sqlite 更合适。
  • BerkeleyDB 变化无常且难以管理,尤其是在处理大量数据的情况下。即使对于单个字符串->字符串存储(这就是 BerkeleyDB 的用途),我也会使用 SQLite,它会负责所有 BerkeleyDB 管理。
  • SQLite 不像字典。
  • bsddb 模块 (python.org/doc/2.6/library/bsddb.html) 的 Python 页面说它已被弃用。 BSD DB 是否还有另一个不推荐使用的 Python 选项?
  • python.org/doc/2.6/library/persistence.html 列出了许多数据持久性模块。 gdbm 模块看起来非常相似并且仍然受支持,我会选择那个。
【解决方案2】:

您真的需要内存中的全部数据吗?如果您想要字典/pickle 方法,您可以以简单的方式拆分它,例如每年或每月一个文件。

另外,请记住字典没有排序,您可能会遇到必须对大量数据进行排序的问题。如果您想搜索或排序数据,当然...

无论如何,我认为之前评论的数据库方法是最灵活的一种,特别是从长远来看...

【讨论】:

    【解决方案3】:

    如果您的真的,真的想要使用类似语义的字典,请尝试 SQLAlchemy 的 associationproxy。以下(相当长的)代码将您的字典翻译成entries-Table 中的Key,Value-Pairs。我不知道 SQLAlchemy 是如何处理你的大字典的,但是 SQLite 应该能够很好地处理它。

    from sqlalchemy import create_engine, MetaData
    from sqlalchemy import Table, Column, Integer, ForeignKey, Unicode, UnicodeText
    from sqlalchemy.orm import mapper, sessionmaker, scoped_session, Query, relation
    from sqlalchemy.orm.collections import column_mapped_collection
    from sqlalchemy.ext.associationproxy import association_proxy
    from sqlalchemy.schema import UniqueConstraint
    
    engine = create_engine('sqlite:///newspapers.db')
    
    metadata = MetaData()
    metadata.bind = engine
    
    Session = scoped_session(sessionmaker(engine))
    session = Session()
    
    newspapers = Table('newspapers', metadata,
        Column('newspaper_id', Integer, primary_key=True),
        Column('newspaper_name', Unicode(128)),
    )
    
    entries = Table('entries', metadata,
        Column('entry_id', Integer, primary_key=True),
        Column('newspaper_id', Integer, ForeignKey('newspapers.newspaper_id')),
        Column('entry_key', Unicode(255)),
        Column('entry_value', UnicodeText),
        UniqueConstraint('entry_key', 'entry_value', name="pair"),
    )
    
    class Base(object):
    
        def __init__(self, **kw):
            for key, value in kw.items():
                setattr(self, key, value)
    
        query = Session.query_property(Query)
    
    def create_entry(key, value):
        return Entry(entry_key=key, entry_value=value)
    
    class Newspaper(Base):
    
        entries = association_proxy('entry_dict', 'entry_value',
            creator=create_entry)
    
    class Entry(Base):
        pass
    
    mapper(Newspaper, newspapers, properties={
        'entry_dict': relation(Entry,
            collection_class=column_mapped_collection(entries.c.entry_key)),
    })
    mapper(Entry, entries)
    
    metadata.create_all()
    
    dictionary = {
        u'foo': u'bar',
        u'baz': u'quux'
    }
    
    roll = Newspaper(newspaper_name=u"The Toilet Roll")
    session.add(roll)
    session.flush()
    
    roll.entries = dictionary
    session.flush()
    
    for entry in Entry.query.all():
        print entry.entry_key, entry.entry_value
    session.commit()
    
    session.expire_all()
    
    print Newspaper.query.filter_by(newspaper_id=1).one().entries
    

    给予

    foo bar
    baz quux
    {u'foo': u'bar', u'baz': u'quux'}
    

    【讨论】:

    • 我正在考虑在 python 中使用 SQLite3。不过,我不确定您的回答中与sqlalchemy 的关系是什么。
    【解决方案4】:

    一种解决方案是使用buzhug 代替pickle。这是一个纯 Python 解决方案,并保留了非常 Pythonic 的语法。我认为这是从搁置及其同类产品升级的下一步。它将处理您正在谈论的数据大小。它的大小限制为每个字段 2 GB(每个字段存储在单独的文件中)。

    【讨论】:

      【解决方案5】:

      我从http://coverartarchive.org 捕获图像,虽然下载这么多图像很慢,但pickle 155 MB 没有问题:

      $ ll
      total 151756
      -rw-rw-r--  1 rick rick 155208082 Oct 10 10:04 ipc.pickle
      

      当我不再只为一张 CD 下载图像时,我会回来并用更大的泡菜限制更新这个答案。不幸的是,我还没有找到任何说明酸洗限制的地方......

      【讨论】:

        猜你喜欢
        • 2011-06-21
        • 2012-06-28
        • 1970-01-01
        • 1970-01-01
        • 2017-07-14
        • 1970-01-01
        • 2017-10-18
        • 1970-01-01
        相关资源
        最近更新 更多