1. ホーム
  2. python

[解決済み] 巨大な .csv ファイルを読み込む

2022-06-14 03:30:47

質問

現在、Python 2.7で.csvファイルから最大100万行、200列のデータを読み込もうとしています(ファイルの範囲は100mbから1.6gbまでです)。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. もっと速くする方法はないでしょうか?

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

どのように解決するのですか?

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

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

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

現在、あなたが持っているのは 一行 をメモリに保持するようになりました。

yield は、ある関数を ジェネレータ関数 を生成します。これは、ループを開始するまで何もしないことを意味します。