web-dev-qa-db-ja.com

python

だから私はかなり巨大な.gzファイルをいくつか持っています-解凍するとそれぞれ10から20 GBです。

それらの各行をループする必要があるので、私は標準を使用しています:

_import gzip
f = gzip.open(path+myFile, 'r')
for line in f.readlines():
    #(yadda yadda)
f.close()
_

ただし、open()コマンドとclose()コマンドは両方ともAGESを使用し、メモリ+ CPUの最大98%を使用します。プログラムが終了し、Killedを端末に出力するほどです。多分それはメモリに抽出されたファイル全体をロードしていますか?

私は今次のようなものを使用しています:

_from subprocess import call
f = open(path+'myfile.txt', 'w')
call(['gunzip', '-c', path+myfile], stdout=f)
#do some looping through the file
f.close()
#then delete extracted file
_

これは機能します。しかし、よりクリーンな方法はありますか?

16

あなたの問題はgzip.open()ではなくreadlines()にあると99%確信しています。

ドキュメント の説明:

f.readlines()は、ファイル内のすべてのデータ行を含むリストを返します。

明らかに、これにはファイル全体を読み取って解凍し、完全に巨大なリストを作成する必要があります。

最も可能性が高いのは、実際には、メモリを永久に割り当てるためのmalloc呼び出しです。そして、このスコープの最後で(CPythonを使用していると仮定して)、その巨大なリスト全体をGCする必要がありますが、これにも時間がかかります。

readlinesを使用することはほとんどありません。非常に古いPythonを使用している場合を除き、次のようにします。

_for line in f:
_

fileは、listによって返されるreadlinesと同じように、反復可能な行でいっぱいです—実際にはlistではないことを除いて、バッファから読み取ることでフライ。したがって、いつでも、25 GBのlistではなく、1行とそれぞれ10MB程度のバッファが2つだけあります。そして、読み取りと圧縮解除は、一度にすべて行われるのではなく、ループの存続期間にわたって分散されます。

3.5GBのgzipファイルを使用した簡単なテストから、gzip.open()は事実上瞬時であり、_for line in f: pass_は数秒かかり、gzip.close()は事実上瞬時です。しかし、for line in f.readlines(): passを実行すると、時間がかかります…まあ、どれくらい時間がかかるのかわかりません。約1分後に、システムがスワップスラッシュヘルになり、インタープリターを強制終了しなければなりませんでした。何にでも対応して…


これはこの回答以来、さらに数十回登場しているので、私は このブログ投稿 と書いて、もう少し説明します。

58
abarnert

pandas、特にIO tools を見てください。これらはファイルを読み取るときにgzip圧縮をサポートし、チャンクでファイルを読み取ることができます。さらにpandasは非常に高速でメモリ効率が良いです。

私が試したことがないので、チャンクでの圧縮と読み取りがどれほどうまく共存しているかはわかりませんが、試してみる価値があるかもしれません

2