現在、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ファイルに一緒にリストされるため、時間を節約するためにそれらを通過するときにループを終了するためです。
私の質問は:
これを大きなファイルで動作させるにはどうすればよいですか?
もっと速くする方法はありますか?
私のコンピューターには64ビットのWindows 7を実行する8 GBのRAMが搭載されており、プロセッサーは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
基準ごとに数千行ではなく、メモリに1行だけを保持するようになりました。
yield
は、関数を generator function にします。つまり、ループ処理を開始するまで作業を行いません。
Martijinの答えは最高です。初心者向けの大きなcsvファイルを処理する、より直感的な方法を次に示します。これにより、行のグループ、またはチャンクを一度に処理できます。
import pandas as pd
chunksize = 10 ** 8
for chunk in pd.read_csv(filename, chunksize=chunksize):
process(chunk)
かなりの量の振動解析を行い、大規模なデータセット(数千億から数億ポイント)を調べます。私のテストでは、 pandas.read_csv() 関数が20であることが示されましたnumpy.genfromtxt()よりも何倍も高速です。また、genfromtxt()関数はnumpy.loadtxt()よりも3倍高速です。大規模なデータセットの場合、needpandasのようです。
このテストで使用したコードとデータセットをブログに投稿しました 振動解析のためのMATLAB vs Python 。
私のために働いたのは超高速です
import pandas as pd
import dask.dataframe as dd
import time
t=time.clock()
df_train = dd.read_csv('../data/train.csv', usecols=[col1, col2])
df_train=df_train.compute()
print("load train: " , time.clock()-t)
別の実用的なソリューションは次のとおりです。
import pandas as pd
from tqdm import tqdm
PATH = '../data/train.csv'
chunksize = 500000
traintypes = {
'col1':'category',
'col2':'str'}
cols = list(traintypes.keys())
df_list = [] # list to hold the batch dataframe
for df_chunk in tqdm(pd.read_csv(PATH, usecols=cols, dtype=traintypes, chunksize=chunksize)):
# Can process each chunk of dataframe here
# clean_data(), feature_engineer(),fit()
# Alternatively, append the chunk to list and merge all
df_list.append(df_chunk)
# Merge all dataframes into one dataframe
X = pd.concat(df_list)
# Delete the dataframe list to release memory
del df_list
del df_chunk
この質問に答える人のために。 pandas と 'chunksize'および 'usecols」は、提案された他のオプションよりも高速に巨大なZipファイルを読み取るのに役立ちました。
import pandas as pd
sample_cols_to_keep =['col_1', 'col_2', 'col_3', 'col_4','col_5']
# First setup dataframe iterator, ‘usecols’ parameter filters the columns, and 'chunksize' sets the number of rows per chunk in the csv. (you can change these parameters as you wish)
df_iter = pd.read_csv('../data/huge_csv_file.csv.gz', compression='gzip', chunksize=20000, usecols=sample_cols_to_keep)
# this list will store the filtered dataframes for later concatenation
df_lst = []
# Iterate over the file based on the criteria and append to the list
for df_ in df_iter:
tmp_df = (df_.rename(columns={col: col.lower() for col in df_.columns}) # filter eg. rows where 'col_1' value grater than one
.pipe(lambda x: x[x.col_1 > 0] ))
df_lst += [tmp_df.copy()]
# And finally combine filtered df_lst into the final lareger output say 'df_final' dataframe
df_final = pd.concat(df_lst)
python3の別のソリューションを次に示します。
import csv
with open(filename, "r") as csvfile:
datareader = csv.reader(csvfile)
count = 0
for row in datareader:
if row[3] in ("column header", criterion):
doSomething(row)
count += 1
Elif count > 2:
break
ここdatareader
はジェネレーター関数です。