2011-10-03 24 views
10

I recently asked a question大きなpythonオブジェクトをファイルに保存する方法は、私は以前、大規模なPython辞書を文字列に変換し、write()を介してファイルに書き込む際に問題に遭遇しました。今私はピクルスを使用しています。それは動作しますが、ファイルは非常に大きいです(> 5 GB)。私はそのような大きなファイルの分野ではほとんど経験がありません。私は、このピクルファイルをメモリに保存する前に、このピックファイルを圧縮する方が早いか、可能であるかどうかを知りたかったのです。大きなファイルをPythonで保存する最速の方法

+0

メモリに保存していますか? – Cameron

+4

@JBernardoそれはあなたの車が壊れたときにあなたが行く必要がある場所に乗るためにあなたの両親に尋ねることができ、その後公共の交通機関を利用して戻ってくるようなものです。 –

+0

それをハードディスクに保存します。私は、ハードディスクに5GBを超える書き込みには非常に時間がかかると仮定しています。 – puk

答えて

3

データのシリアル化を実装する場合、Pythonコードが非常に遅くなります。 純粋なPythonでPickleに相当するものを作成しようとすると、それは超低速になることがわかります。 幸いにもそれを実行する組み込みモジュールはかなり良いです。

cPickleの他に、marshalモジュールがあります。これははるかに高速です。 しかし、ファイルのようなオブジェクトからではなく、実際のファイルハンドルが必要です。 import marshal as Pickleと違いを見ることができます。私はあなたがこれよりもはるかに高速であるカスタム・シリアライザを作ることができるとは思わない ...

はここで実際の(それほど古くない)だ serious benchmark of Python serializers

+0

に書かれている通り、基本的には、それを圧縮、それは私が何を探しています何だと思いますか私は簡単にマーシャリングに切り替えることができますまたはそれがストリームにデータ構造をダンプする大規模なオーバーホール – puk

+0

は*連載*である必要があります。簡単にcPickle/marshalに切り替えることができます(私はあなたがcPickleを使っていると思いました)。 –

+0

いいえいいえ、私はプレーンピックル(ppickle?)を使用していました。私はマーシャルとピクルスとWOWを比較した単純なテストを実行しました。我々は2桁の大きさの違いを話しています。 – puk

9

あなたはbzip2でデータを圧縮することができます

from __future__ import with_statement # Only for Python 2.5 
import bz2,json,contextlib 

hugeData = {'key': {'x': 1, 'y':2}} 
with contextlib.closing(bz2.BZ2File('data.json.bz2', 'wb')) as f: 
    json.dump(hugeData, f) 

このようにそれをロードします。

from __future__ import with_statement # Only for Python 2.5 
import bz2,json,contextlib 

with contextlib.closing(bz2.BZ2File('data.json.bz2', 'rb')) as f: 
    hugeData = json.load(f) 

をあなたもほとんど同じインターフェースでzlibまたはgzipを使用してデータを圧縮することができます。しかし、zlibとgzipの両方の圧縮率は、bzip2(またはlzma)で得られる圧縮率よりも低くなります。

+0

私はその構文に慣れていませんが、BZ2Fileをメモリに保存してから、json(またはpickle私は仮定しています)経由でダンプできます。 – puk

+0

@pook構文は何ですか? 'with'ステートメントは、ファイルが確実に閉じられるようにする文法的な砂糖です。 'f = bz2.BZ2File(...)'と読むことができます。これは、大量のデータをメモリに保存するのではなく**、良いことです。データはシリアル化され、オンザフライで圧縮され、メモリー使用量(OSキャッシュを差し引いたもの)はメガバイトよりはるかに大きくすべきではありません。 – phihag

+0

'with'アプローチでは、はるかに少ないメモリしか使用しません。 – puk

1

速く、あるいは可能性は、前に[書き込み]もちろん

このピクルスファイルを圧縮することは可能ですが、(メモリ内の明示的なzip形式のコピーを作成することがありますしようとする理由はありませんあなたがが書かれているように自動的に書かれているように、標準ライブラリ機能を内蔵していれば、;

http://docs.python.org/library/gzip.htmlを参照してください。基本的に、あなたは

gzip.GzipFile("output file name", "wb") 

でストリームの特別な種類を作成し、(そのことについてまたはfile(...)open(...)で作成した通常のfileのように正確にそれを使用しています。

+0

私はこれを求めて本当にばかに感じるが、なぜ「シリアライズ」であり、かつWOW本当に100倍高速漬物よりもマーシャルのですか?それは – puk

-1

GoogleのProtoBuffersをご覧ください。これらはオーディオ・ビデオ・ファイルのような大規模なファイル用には設計されていませんが、オブジェクト用に設計されているため、オブジェクトの直列化には適しています。練習では、いつかファイルの構造を更新する必要があり、ProtoBuffersがそれを処理することを示しています。また、圧縮とスピードのために高度に最適化されています。そしてあなたはPythonに縛られていないので、JavaとC++はうまくサポートされています。

1

私はちょうどphihagの答えを拡張したいです。

RAMのサイズに近いオブジェクトをシリアル化しようとすると、シリアル化するためにとなるので、のpickle/cPickleは避けるべきです。それをBZ2ファイルにストリーミングするときにも当てはまります。私の場合、私はスワップスペースを使い果たしていました。

しかし、JSONの問題(リンクされた記事で述べたようなHDFファイルと同様)は、データ内のタプルをシリアル化できないということです。データはdictsのキーとして使用されます。これには大きな解決策はありません。私が見つけた最高のものは、タプルを文字列に変換することでした。文字列には、それ自身のメモリが必要ですが、ピクルスよりもはるかに少ないものでした。現在、the ujson libraryを使用することもできます。これはjsonライブラリよりもはるかに高速です。文字列からなるタプルについて

(無コンマを含まないために文字列を必要とする):

import ujson as json 
from bz2 import BZ2File 

bigdata = { ('a','b','c') : 25, ('d','e') : 13 } 
bigdata = dict([(','.join(k), v) for k, v in bigdata.viewitems()]) 

f = BZ2File('filename.json.bz2',mode='wb') 
json.dump(bigdata,f) 
f.close() 

再構成タプルに:あるいは

bigdata = dict([(tuple(k.split(',')),v) for k,v in bigdata.viewitems()]) 

例えばもし

bigdata2 = { (1,2): 1.2, (2,3): 3.4} 
bigdata2 = dict([('%d,%d' % k, v) for k, v in bigdata2.viewitems()]) 
# ... save, load ... 
bigdata2 = dict([(tuple(map(int,k.split(','))),v) for k,v in bigdata2.viewitems()]) 

漬物を超えるこのアプローチのもう一つの利点は、JSONは、bzip2圧縮を使用した場合漬物よりも有意に良好に圧縮するように見えるということである:あなたの鍵は、整数の2要素のタプルです。

関連する問題