2010-01-21 1 views
4

мой друг написал эту небольшую прогамму. размер textFile составляет 1,2 ГБ (газета за 7 лет). Ему удалось создать словарь, но он не может записать его в файл с помощью pickle (зависает программа).Сохранение огромного словаря bigram в файл с использованием 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> являются приговоры сепараторов.
  • одно слово в строке.

a biGramDictionary создан для последующего использования.
что-то вроде этого:

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

надеюсь, что это помогает. в настоящее время стратегия изменилась на использование mysql, потому что sqlite просто не работал (вероятно, из-за размера)

+0

Если вы собираетесь возиться с большими файлами, почему бы не использовать базу данных? Кроме того, я вижу, что вы делаете цикл за один файл 2 раза, что может быть избыточным и добавляет к стоимости обработки. почему бы не описать, что вы делаете с образцами входных файлов? – ghostdog74

+1

ghostdog74, вы видите 2 для операторов, но есть только один цикл над файлом :) Итерирование по файлу - это просто чтение строк (из фактической позиции), оно не стремится к началу файла. – Messa

+0

Просто попробуйте [sqlitedict] (https://pypi.python.org/pypi/sqlitedict) (ваш Python dict, поддерживаемый БД на диске, а не ОЗУ). – Radim

ответ

10

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

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

+0

Sqlite - полностью реляционная база данных, а Berkeley DB - это не просто ключ/значение. Если он просто хранится, я думаю, что Berkeley - лучший вариант, а если вы хотите сделать несколько запросов и сохранить информацию более организованным способом, то sqlite будет более подходящим. – Khelben

+1

BerkeleyDB довольно изменчив и трудно управляется, особенно с большим количеством данных. Даже для одного файла string-> string (каким будет BerkeleyDB) я бы использовал SQLite, который позаботится обо всем управлении BerkeleyDB. –

+0

SQLite не действует как словарь. –

1

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

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

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

0

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

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

Смежные вопросы