135

現在、最大 100 万行、200 列 (ファイルの範囲は 100mb から 1.6gb) の Python 2.7 の .csv ファイルからデータを読み込もうとしています。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 です (必要な情報が不明です)。

4

7 に答える 7

176

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

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

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関数をジェネレーター関数にします。つまり、ループを開始するまで何も機能しません。

于 2013-07-03T09:50:41.667 に答える
9

私のために働いたのは超高速です

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
于 2018-05-31T12:42:33.727 に答える