使用pickle将巨大的二元字典保存到文件
Posted
技术标签:
【中文标题】使用pickle将巨大的二元字典保存到文件【英文标题】:Saving huge bigram dictionary to file using 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>
<s>
是句子分隔符。
每行一个字。
生成一个 biGramDictionary 供以后使用。 像这样:
"Hello": "World": 1, "munde": 1,
"World": "domination": 2,
"Total": "World": 1,
希望这会有所帮助。现在策略改为使用 mysql,因为 sqlite 不起作用(可能是因为大小)
【问题讨论】:
如果您要处理大文件,为什么不使用数据库呢?另外,我看到你在同一个文件上循环了 2 次,这可能是多余的并增加了处理成本。为什么不描述您对示例输入文件所做的事情? ghostdog74,你看到 2 个 for 语句,但文件上只有一个循环 :) 迭代文件只是读取行(从实际位置),它不会寻找到开头文件。 只需尝试sqlitedict(您的 Python 字典由磁盘上的 DB 支持,而不是 RAM)。 【参考方案1】:Pickle 仅用于编写完整的(小)对象。您的字典有点大,甚至无法存储在内存中,您最好使用数据库代替,这样您就可以一个一个地存储和检索条目,而不是一次全部存储和检索。
可以在 Python 中使用的一些良好且易于集成的单一文件数据库格式是 SQLite 或 DBM 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 下载图像时,我会回来并用更大的泡菜限制更新这个答案。不幸的是,我还没有找到任何说明酸洗限制的地方......
【讨论】:
以上是关于使用pickle将巨大的二元字典保存到文件的主要内容,如果未能解决你的问题,请参考以下文章
保存数据到文件的模块(configparser,json,pickle,shelve,xml)_python
为啥 pickle.dump(obj) 与 sys.getsizeof(obj) 的大小不同?如何将变量保存到文件文件?