Сохранение огромного словаря биграмм в файл с помощью pickle - PullRequest
6 голосов
/ 21 января 2010

мой друг написал эту маленькую прогаму. textFile имеет размер 1,2 ГБ (газеты за 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)

спасибо заранее.

EDIT
для тех, кто заинтересован, я кратко объясню, что делает эта программа. при условии, что у вас есть файл, сформатированный примерно так:

<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 просто не работал (возможно, из-за размера)

Ответы [ 4 ]

10 голосов
/ 21 января 2010

Pickle предназначен только для написания полных (маленьких) объектов. Ваш словарь немного велик, чтобы его можно было даже хранить в памяти, вместо этого лучше использовать базу данных, чтобы вы могли хранить и извлекать записи по одной, а не по всем сразу.

Некоторые хорошие и легко интегрируемые форматы базы данных с одним файлом, которые вы можете использовать в Python: SQLite или один из DBM . Последний действует как словарь (то есть вы можете читать и записывать пары ключ / значение), но использует диск в качестве хранилища, а не 1,2 ГБ памяти.

1 голос
/ 21 января 2010

Одним из решений является использование бужуг вместо маринада. Это чисто Python-решение, и оно сохраняет очень Pythonic синтаксис. Я думаю об этом как о следующем шаге от полки и тому подобного. Он будет обрабатывать размеры данных, о которых вы говорите. Ограничение его размера составляет 2 ГБ на поле (каждое поле хранится в отдельном файле).

1 голос
/ 21 января 2010

Вам действительно нужны все данные в памяти? Вы можете разделить его наивными способами, например, одним файлом для каждого года или каждого месяца, если вы хотите использовать словарь / рассол.

Кроме того, помните, что словари не отсортированы, у вас могут возникнуть проблемы с сортировкой такого количества данных. Если вы хотите искать или сортировать данные, конечно ...

Во всяком случае, я думаю, что подход к базе данных, прокомментированный ранее, является наиболее гибким, особенно в долгосрочной перспективе ...

0 голосов
/ 21 января 2010

Если ваш действительно, действительно хочет использовать словарь типа семантики, попробуйте SQLAlchemy's associationproxy. Следующий (довольно длинный) фрагмент кода переводит ваш словарь в Key, Value-Pairs в таблице entries. Я не знаю, как 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

1010 * дает *

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