2010-01-21 3 views
4

친구가이 작은 프로그램을 작성했습니다. textFile의 크기는 1.2GB (7 년 분량의 신문)입니다. 그는 사전을 성공적으로 생성 할 수는 있지만 pickle (프로그램이 멈춤)을 사용하여 파일에 쓸 수는 없습니다.피클을 사용하여 거대한 bigram 사전을 파일로 저장

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}, 
} 

희망. 지금은 sqlite가 작동하지 않아서 mysql을 사용하는 것으로 바뀌었다. (아마도 크기 때문에)

+0

큰 파일을 망칠 경우 데이터베이스를 사용하지 않으시겠습니까? 또한, 같은 파일을 두 번 반복하는 루프를 참조하십시오. 중복 될 수 있으며 처리 비용이 추가됩니다. 샘플 입력 파일로 무엇을하고 있는지 설명하지 않으시겠습니까? – ghostdog74

+1

ghostdog74, 명령문에 2가 표시되지만 파일에 루프가 하나만 있습니다. 파일을 반복하면 실제 위치에서 행을 읽는 것만으로 파일 시작 부분을 찾지 않습니다. – Messa

+0

간단히 [sqlitedict] (https://pypi.python.org/pypi/sqlitedict) (RAM이 아닌 디스크상의 DB가 지원하는 Python dict)을 사용해보십시오. – Radim

답변

10

Pickle은 완전한 (작은) 객체를 작성하기위한 용도로만 사용되었다. 당신의 사전은 약간 메모리가 넉넉하기 때문에 데이터베이스를 대신 사용하여 한 번에 하나씩 항목을 저장하고 검색 할 수 있습니다.

파이썬에서 사용할 수있는 훌륭하고 쉽게 통합 할 수있는 단일 파일 데이터베이스 형식은 SQLite이거나 DBM variants 중 하나입니다. 마지막 하나는 사전과 마찬가지로 작동하지만 (키/값 쌍을 읽고 쓸 수 있음) 1.2GB의 메모리가 아닌 저장소로 디스크를 사용합니다.

+0

Sqlite는 완전히 관계형 데이터베이스이지만 Berkeley DB는 키/값이 아닙니다. 그냥 저장하는 경우 버클리 더 나은 옵션, 일부 쿼리를 만들고 좀 더 조직적인 방법으로 정보를 저장하려는 sqlite 더 적절하다고 생각합니다. – Khelben

+1

BerkeleyDB는 특히 대량의 데이터를 관리 할 때 다소 변덕스럽고 관리하기가 어렵습니다. 단일 문자열 -> 문자열 저장소 (BerkeleyDB가 될 것임)의 경우에도 모든 BerkeleyDB 관리를 처리 할 SQLite를 사용합니다. –

+0

SQLite는 사전처럼 작동하지 않습니다. –

1

전체 데이터가 실제로 메모리에 필요합니까? 사전/피클 접근법을 원한다면 매달 한 번씩 파일을 순진한 방법으로 분할 할 수 있습니다.

또한 사전이 정렬되지 않는다는 것을 기억하십시오. 데이터를 대량으로 정렬해야 할 때 문제가 발생할 수 있습니다. 혹시

0

만약 ... 내가 전에 주석 데이터베이스 접근 방식은 특별히 긴 안목에서 가장 유연한이라고 생각 ... 어쨌든,

을 검색하거나 물론, 데이터를 정렬하려는 정말로, 정말로 의미론과 같은 사전을 사용하고 싶다면 SQLAlchemy의 associationproxy을 사용해보십시오. 다음 (다소 긴) 코드는 사전을 키, 값 쌍 (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 

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

하나의 솔루션 대신 피클의 buzhug을 사용하는 것입니다 제공합니다. 그것은 순수한 Python 솔루션이며 매우 Pythonic 구문을 유지합니다. 나는 이것을 선반과 ilk에서 다음 단계로 생각합니다.그것은 당신이 말하는 데이터 크기를 처리 할 것입니다. 크기 제한은 필드 당 2GB입니다 (각 필드는 별도의 파일에 저장됩니다).

관련 문제