巨大な.csvファイルの読み取り 質問する

巨大な.csvファイルの読み取り 質問する

現在、Python 2.7 で最大 100 万行、200 列の .csv ファイルからデータを読み取ろうとしています (ファイルの範囲は 100 MB から 1.6 GB)。300,000 行未満のファイルでは (非常に低速ですが) 実行できますが、それ以上になるとメモリ エラーが発生します。コードは次のようになります。

def getdata(filename, criteria):
    data=[]
    for criterion in criteria:
        data.append(getstuff(filename, criteron))
    return data

def getstuff(filename, criterion):
    import csv
    data=[]
    with open(filename, "rb") as csvfile:
        datareader=csv.reader(csvfile)
        for row in datareader: 
            if row[3]=="column header":
                data.append(row)
            elif len(data)<2 and row[3]!=criterion:
                pass
            elif row[3]==criterion:
                data.append(row)
            else:
                return data

getstuff 関数に else 句がある理由は、基準に適合するすべての要素が csv ファイルに一緒にリストされるため、それらを通過したらループを終了して時間を節約するためです。

私の質問は次のとおりです:

  1. どうすれば、これをより大きなファイルで動作させることができるでしょうか?

  2. もっと早くする方法はありますか?

私のコンピュータには 8 GB の RAM があり、64 ビット Windows 7 を実行しており、プロセッサは 3.40 GHz です (必要な情報がわかりません)。

ベストアンサー1

すべての行をリストに読み込み、そのリストを処理します。そんなことしないで

行を生成するときに処理します。最初にデータをフィルタリングする必要がある場合は、ジェネレーター関数を使用します。

import csv

def getstuff(filename, criterion):
    with open(filename, "rb") as csvfile:
        datareader = csv.reader(csvfile)
        yield next(datareader)  # yield the header row
        count = 0
        for row in datareader:
            if row[3] == criterion:
                yield row
                count += 1
            elif count:
                # done when having read a consecutive series of rows 
                return

フィルター テストも簡略化しました。ロジックは同じですが、より簡潔になっています。

条件に一致する行の単一のシーケンスのみを照合するため、次のコードも使用できます。

import csv
from itertools import dropwhile, takewhile

def getstuff(filename, criterion):
    with open(filename, "rb") as csvfile:
        datareader = csv.reader(csvfile)
        yield next(datareader)  # yield the header row
        # first row, plus any subsequent rows that match, then stop
        # reading altogether
        # Python 2: use `for row in takewhile(...): yield row` instead
        # instead of `yield from takewhile(...)`.
        yield from takewhile(
            lambda r: r[3] == criterion,
            dropwhile(lambda r: r[3] != criterion, datareader))
        return

これで直接ループできるようになりましたgetstuff()。 でも同じことを行いますgetdata():

def getdata(filename, criteria):
    for criterion in criteria:
        for row in getstuff(filename, criterion):
            yield row

getdata()コード内で直接ループします。

for row in getdata(somefilename, sequence_of_criteria):
    # process row

あなたは今、1行基準ごとに数千行ではなく、メモリに格納されます。

yield関数をジェネレータ関数つまり、ループを開始するまで何も実行されません。

おすすめ記事