3

テキストファイル内の文字列の出現回数をカウントしようとしています。テキストファイルは次のようになり、各ファイルは約200MBです。

String1 30
String2 100
String3 23
String1 5
.....

カウントをdictに保存したいと思います。

count  = {}
for filename in os.listdir(path):
    if(filename.endswith("idx")):
        continue
    print filename  
    f = open(os.path.join(path, filename))
    for line in f:
        (s, cnt) = line[:-1].split("\t")
        if(s not in count):
            try:
                count[s] = 0 
            except MemoryError:
                print(len(count))
                exit()
        count[s] += int(cnt)  
    f.close()
    print(len(count))

でメモリエラーが発生しましcount[s] = 0たが、コンピュータにまだはるかに多くのメモリがあります。
この問題を解決するにはどうすればよいですか?ありがとうございました!

更新:実際のコードをここにコピーしました。私のPythonバージョンは2.4.3で、マシンはLinuxを実行しており、約48Gのメモリを搭載していますが、消費するのは5G未満です。コードはで停止しlen(count)=44739243ます。

UPDATE2:文字列は複製される可能性があるため(一意の文字列ではない)、文字列のすべてのカウントを合計したいと思います。私が欲しい操作は、各文字列のカウントを読み取ることです。各ファイルあたり約1,000万行あり、30を超えるファイルがあります。数は1000億未満だと思います。

UPDATE3:OSはLinux2.6.18です。

4

3 に答える 3

4

cPython 2.4 では、x64 でも大きなメモリ割り当てで問題が発生する可能性があります。

$ python2.4 -c "'a' * (2**31-1)"
Traceback (most recent call last):
  File "<string>", line 1, in ?
MemoryError
$ python2.5 -c "'a' * (2**31-1)"
$

これらの問題を回避するには、最新の Python インタープリター (cPython 2.7 など) に更新し、64 ビット バージョンのインタープリターをインストールしてください。

文字列のサイズが自明でない場合 (つまり、この例では 10 バイト未満)、代わりに単純にハッシュを格納するか、ブルーム フィルターのような確率論的 (ただしより効率的な) ストレージを使用することもできます。ハッシュを保存するには、ファイル処理ループを次のように置き換えます

import hashlib
# ...
for line in f:
    s, cnt = line[:-1].split("\t")
    idx = hashlib.md5(s).digest()
    count[idx] = count.get(idx, 0) + int(cnt)
# ...
于 2012-10-03T22:43:24.677 に答える
1

なぜこのクラッシュが発生するのかよくわかりません。文字列の推定平均サイズはどれくらいですか?4,400万の文字列。多少長い場合は、すでに提案されているように、ハッシュ化を検討する必要があります。欠点は、一意のキーを一覧表示するオプションが失われることです。データに文字列が含まれているかどうかを確認するだけです。

すでに5GBに達しているメモリ制限に関しては、古いPythonバージョンに関連している可能性があります。更新するオプションがある場合は、2.7を入手してください。同じ構文(およびいくつかの追加機能)、問題なし。ええと、次のコードがまだ2.4と互換性があるかどうかさえわかりません。おそらく、with-statementをもう一度キックアウトする必要があります。少なくとも、これは2.7での記述方法です。

お使いのバージョンとの主な違いは、ガベージコレクションを手動で実行することです。さらに、Pythonが使用するメモリ制限を引き上げることができます。あなたが言ったように、それは実際のRAMのごく一部しか使用しないので、それが大きくなることを禁止する奇妙なデフォルト設定がある場合は、これを試してください:

MEMORY_MB_MAX = 30000
import gc
import os
import resource
from collections import defaultdict
resource.setrlimit(resource.RLIMIT_AS, (MEMORY_MB_MAX * 1048576L, -1L))

count  = defaultdict(int)
for filename in os.listdir(path):
    if(filename.endswith("idx")):
        continue
    print filename  
    with open(os.path.join(path, filename)) as f:
        for line in f:
            s, cnt = line[:-1].split("\t")
            count[s] += int(cnt)  
    print(len(count))
    gc.collect()

それ以外に、私はあなたの行の意味を理解していませんs, cnt = line[:-1].split("\t")、特に[:-1]。あなたが指摘したようにファイルが見える場合、これはあなたの番号の最後の桁を消去します。これはわざとですか?

于 2012-10-03T22:47:38.150 に答える
1

一意の文字列の数をカウントするだけの場合は、各文字列をハッシュすることでメモリ フットプリントを大幅に削減できます。

    (s, cnt) = line[:-1].split("\t")
    s = hash(s)
于 2012-10-03T22:43:43.790 に答える