現在、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 ファイルに一緒にリストされるため、それらを通過したらループを終了して時間を節約するためです。
私の質問は次のとおりです:
どうすれば、これをより大きなファイルで動作させることができるでしょうか?
もっと早くする方法はありますか?
私のコンピュータには 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
関数をジェネレータ関数つまり、ループを開始するまで何も実行されません。