パンダで大きなcsvファイルを読み取るにはどうすればよいですか?


194

パンダで大きなcsvファイル(約6 GB)を読み取ろうとすると、メモリエラーが発生します。

MemoryError                               Traceback (most recent call last)
<ipython-input-58-67a72687871b> in <module>()
----> 1 data=pd.read_csv('aphro.csv',sep=';')

...

MemoryError: 

これについて何か助けはありますか?


3
奇妙なことに、この質問のほぼ1年前に非常によく似た質問出されました...
DarkCygnus


これはあなたの質問に答えますか?パンダを使用した「大容量データ」ワークフロー
AMC

回答:


261

このエラーは、マシンにCSV全体を一度にDataFrameに読み込むための十分なメモリがないことを示しています。メモリ内のデータセット全体を一度にすべて必要としない場合、問題を回避する1つの方法は、CSVをチャンクでchunksizeパラメータを指定して)処理することです。

chunksize = 10 ** 6
for chunk in pd.read_csv(filename, chunksize=chunksize):
    process(chunk)

chunksizeパラメータは、チャンクごとの行数を指定します。(chunksizeもちろん、最後のチャンクには行よりも少ない行が含まれる場合があります。)


17
通常、何かを読み取るには2倍の最終メモリが必要です(csvから、ただし他の形式はメモリ要件が低い方が優れています)。ちなみに、これはほとんどすべてを一度に実行しようとする場合に当てはまります。それをチャンクする方がずっと良いです(メモリ使用量は一定です)。
ジェフ

24
@altabq:ここでの問題は、すべてのデータを保持する単一のDataFrameを構築するのに十分なメモリがないことです。上記の解決策は、チャンクを一度に1つずつ(たとえば、必要な情報のみを集約または抽出することによって)削減することでこの状況に対処しようとするため、メモリを節約できます。何をするにしても、DF.append(chunk)ループ内では呼び出さないでください。それはO(N^2)コピー操作を使用します。集計データをリストに追加し、またはへの1回の呼び出しでリストからDataFrameを構築することをお勧めします(集計データのタイプによって異なります)。pd.DataFramepd.concat
unutbu 2016

12
@altabq:を呼び出すたびに新しいDataFrameが返されるためDF.append(chunk)、ループでの呼び出しでは、チャンクのサイズであるO(N^2)コピー操作が必要です。を呼び出すか、ループの外側で1回呼び出すと、へのコピー量が減少します。NDF.appendpd.DataFramepd.concat O(N)
unutbu 2016

5
@パイダーマン:はい、chunksizeパラメーターはチャンクごとの行数を参照します。chunksizeもちろん、最後のチャンクには行よりも少ない行が含まれる場合があります。
unutbu 2016年

7
@パイダーマン:はい; ループ後のpd.concat([list_of_dfs]) 1回の呼び出し、ループ内での呼び出しpd.concatまたは呼び出しよりもはるかに高速ですdf.append。もちろん、6GBのcsv全体を1つのデータフレームとして保持するには、かなりの量のメモリが必要です。
unutbu 2016年

85

チャンクがこの問題の最初の呼び出しポートであるとは限りません。

  1. 数値以外のデータや不要な列が繰り返されているためにファイルが大きくなっていますか?

    その場合は、列をカテゴリーとして読み取り、pd.read_csv usecolsパラメーターを介して必要な列を選択することにより、大幅なメモリの節約が見られることがあります。

  2. ワークフローには、スライス、操作、エクスポートが必要ですか?

    その場合、dask.dataframeを使用してスライスし、計算を実行して、繰り返しエクスポートできます。チャンクはdaskによってサイレントに実行されます。これは、pandas APIのサブセットもサポートします。

  3. 他のすべてが失敗した場合、チャンクを介して行ごとに読み取ります。

    最後の手段としてパンダ経由またはcsvライブラリ経由でチャンクを作成します


3
ダスクは知らなかった。+100!
noamtm

34

私はこのように進みました:

chunks=pd.read_table('aphro.csv',chunksize=1000000,sep=';',\
       names=['lat','long','rf','date','slno'],index_col='slno',\
       header=None,parse_dates=['date'])

df=pd.DataFrame()
%time df=pd.concat(chunk.groupby(['lat','long',chunk['date'].map(lambda x: x.year)])['rf'].agg(['sum']) for chunk in chunks)

22
からread_csvに切り替えた理由はありますread_tableか?
Pyderman、

33

大きなデータの場合は、ライブラリ「dask」を使用することをお勧めします。
例:

# Dataframes implement the Pandas API
import dask.dataframe as dd
df = dd.read_csv('s3://.../2018-*-*.csv')

詳細については、こちらのドキュメントをご覧ください

もう1つの優れた代替手段は、modinを使用することです。すべての機能はパンダと同じですが、daskなどの分散データフレームライブラリを利用します。


11
パンダを超える利点は、いくつかのポインタを追加することを感謝するかもしれません
PirateApp

2
私は長い間Daskを使用していませんでしたが、私の使用例の主な利点は、Daskが複数のマシンで並列に実行でき、データをスライスとしてメモリに収めることができることです。
Simbarashe Timothy Motsi

2
ありがとう!はパンダの代替品を軽く塗るか、パンダの上にレイヤーとして機能しますか
PirateApp 2018

3
ようこそ、Numpy、Pandas、Scikit-Learnのラッパーとして機能します。
Simbarashe Timothy Motsi

1
Daskでいくつかの問題に直面しようとしましたが、すべてに対して常にエラーをスローします。チャンクでもメモリエラーが発生します。stackoverflow.com/questions/59865572/…を
Genarito

10

上記の答えはすでにトピックを満たしています。とにかく、メモリ内のすべてのデータが必要な場合-bcolzを参照してください。メモリ内のデータを圧縮します。私はそれで本当に良い経験をしました。しかし、パンダ機能の多くが欠けています

編集:データの種類にもよりますが、圧縮率は約1/10または元のサイズだと思います。欠けている重要な機能は集合体でした。


2
この回答を改善して、a)取得する圧縮率とb)不足しているパンダの主な機能を教えてください。NAを処理できますか?文字列?カテゴリー?日付?
smci

え?NAを処理できますか?文字列?カテゴリー?日付?これらは、パンダのcsvを遅くてたるんだものにするものです。NAや文字列のようなオブジェクト(短いものでも)はキラーです。ところで、ブログから参照されている.ipynbがダウンしています。
smci

1
@smci私はあなたにメモを読んでいました。しかし、私はあなたがドキュメントを見ておくことをお勧めします。自分で読む必要があります。
PlagTag 2016年

2
NA、文字列、日付を処理できません。フロートも扱えるとは思いません。
smci 2016

1
chunks前述の方法を使用してパンダで前処理し、分析のためにメモリ内のすべてのデータが必要な場合はbcolz を使用できると思います。ちょっとした考え。
JakeCowton 2017年

6

データをチャンクとして読み取り、各チャンクをピクルスとして保存できます。

import pandas as pd 
import pickle

in_path = "" #Path where the large file is
out_path = "" #Path to save the pickle files to
chunk_size = 400000 #size of chunks relies on your available memory
separator = "~"

reader = pd.read_csv(in_path,sep=separator,chunksize=chunk_size, 
                    low_memory=False)    


for i, chunk in enumerate(reader):
    out_file = out_path + "/data_{}.pkl".format(i+1)
    with open(out_file, "wb") as f:
        pickle.dump(chunk,f,pickle.HIGHEST_PROTOCOL)

次のステップでは、ピクルスを読み取り、各ピクルを目的のデータフレームに追加します。

import glob
pickle_path = "" #Same Path as out_path i.e. where the pickle files are

data_p_files=[]
for name in glob.glob(pickle_path + "/data_*.pkl"):
   data_p_files.append(name)


df = pd.DataFrame([])
for i in range(len(data_p_files)):
    df = df.append(pd.read_pickle(data_p_files[i]),ignore_index=True)

3
最終df的に完全にメモリ内に収まり(暗黙のとおり)、入力と同じ量のデータが含まれている場合、確かにまったくチャンクする必要はありませんか?
jpp

この場合、たとえば、ファイルの幅が非常に大きい場合(100列を超え、多くの文字列列がある場合など)は、チャンクにする必要があります。これにより、dfをメモリに保持するために必要なメモリが増加します。このような4GBファイルでも、64 GB RAMのボックスで20〜30 GBのRAMを使用することになります。
cdabel

4

関数read_csvとread_tableはほとんど同じです。ただし、プログラムで関数read_tableを使用する場合は、区切り文字「、」を割り当てる必要があります。

def get_from_action_data(fname, chunk_size=100000):
    reader = pd.read_csv(fname, header=0, iterator=True)
    chunks = []
    loop = True
    while loop:
        try:
            chunk = reader.get_chunk(chunk_size)[["user_id", "type"]]
            chunks.append(chunk)
        except StopIteration:
            loop = False
            print("Iteration is stopped")

    df_ac = pd.concat(chunks, ignore_index=True)

この投稿にあなたの質問が何であるかを述べておくと役に立ちます。「read_csvとread_tableの違いは何ですか?」または「読み取りテーブルに区切り文字が必要なのはなぜですか?」
nate_weldon

1
それはあなたのファイルがどのように見えるかに依存します。一部のファイルには、「、」、「|」などの共通の区切り文字があります または「\ t」ですが、0x01、0x02(これを1つにする)などの区切り文字が含まれる他のファイルが表示される場合があります。そのため、read_tableは一般的でない区切り文字に適していますが、read_csvでも同じように同じことができます。
Naufal

3

解決策1:

大きなデータでパンダを使用する

解決策2:

TextFileReader = pd.read_csv(path, chunksize=1000)  # the number of rows per chunk

dfList = []
for df in TextFileReader:
    dfList.append(df)

df = pd.concat(dfList,sort=False)

3
ここでも、6 GBのファイルを完全にメモリにロードしています。オプションはありますか。現在のチャンクを処理して、次のチャンクを読み取ることができます
debaonline4u

6
しないでdfList.append、各チャンク(df)を個別に処理するだけ
gokul_uf

3

次に例を示します。

chunkTemp = []
queryTemp = []
query = pd.DataFrame()

for chunk in pd.read_csv(file, header=0, chunksize=<your_chunksize>, iterator=True, low_memory=False):

    #REPLACING BLANK SPACES AT COLUMNS' NAMES FOR SQL OPTIMIZATION
    chunk = chunk.rename(columns = {c: c.replace(' ', '') for c in chunk.columns})

    #YOU CAN EITHER: 
    #1)BUFFER THE CHUNKS IN ORDER TO LOAD YOUR WHOLE DATASET 
    chunkTemp.append(chunk)

    #2)DO YOUR PROCESSING OVER A CHUNK AND STORE THE RESULT OF IT
    query = chunk[chunk[<column_name>].str.startswith(<some_pattern>)]   
    #BUFFERING PROCESSED DATA
    queryTemp.append(query)

#!  NEVER DO pd.concat OR pd.DataFrame() INSIDE A LOOP
print("Database: CONCATENATING CHUNKS INTO A SINGLE DATAFRAME")
chunk = pd.concat(chunkTemp)
print("Database: LOADED")

#CONCATENATING PROCESSED DATA
query = pd.concat(queryTemp)
print(query)


2

パンダを使用して大きなファイルをチャンクに読み込んでから行ごとに生成する場合、ここで私がやったことです

import pandas as pd

def chunck_generator(filename, header=False,chunk_size = 10 ** 5):
   for chunk in pd.read_csv(filename,delimiter=',', iterator=True, chunksize=chunk_size, parse_dates=[1] ): 
        yield (chunk)

def _generator( filename, header=False,chunk_size = 10 ** 5):
    chunk = chunck_generator(filename, header=False,chunk_size = 10 ** 5)
    for row in chunk:
        yield row

if __name__ == "__main__":
filename = r'file.csv'
        generator = generator(filename=filename)
        while True:
           print(next(generator))

1

すでに提供されている可能性のあるソリューションのほとんどに基づいて、より包括的な答えを出したいと思います。また、読書プロセスに役立つ可能性のあるもう1つの潜在的な援助を指摘したいと思います。

オプション1:dtypes

「dtypes」は、readメソッドのメモリ負荷を軽減するために使用できるかなり強力なパラメータです。これこの答えを見てください。パンダは、デフォルトで、データのdtypeを推測しようとします。

データ構造を参照すると、保存されているすべてのデータに対して、メモリ割り当てが行われます。基本的なレベルでは、以下の値を参照してください(以下の表は、Cプログラミング言語の値を示しています)。

The maximum value of UNSIGNED CHAR = 255                                    
The minimum value of SHORT INT = -32768                                     
The maximum value of SHORT INT = 32767                                      
The minimum value of INT = -2147483648                                      
The maximum value of INT = 2147483647                                       
The minimum value of CHAR = -128                                            
The maximum value of CHAR = 127                                             
The minimum value of LONG = -9223372036854775808                            
The maximum value of LONG = 9223372036854775807

NumPyとCタイプの一致を確認するには、このページを参照しください。

あなたが数字の整数の配列を持っているとしましょう。理論的にも実際的にも、たとえば16ビット整数型の配列を割り当てることができますが、その配列を格納するために実際に必要なメモリよりも多くのメモリを割り当てることになります。これを防ぐには、にdtypeオプションを設定しますread_csv。実際に配列を8ビット整数(np.int8またはnp.uint8)に合わせることができる長整数として配列項目を保存する必要はありません。

次のdtypeマップを確認してください。

ソース:https : //pbpython.com/pandas_dtypes.html

{column:type}のようにdtypedictsとしてpandasメソッドのパラメーターとしてパラメーターを渡すことができますread

import numpy as np
import pandas as pd

df_dtype = {
        "column_1": int,
        "column_2": str,
        "column_3": np.int16,
        "column_4": np.uint8,
        ...
        "column_n": np.float32
}

df = pd.read_csv('path/to/file', dtype=df_dtype)

オプション2:チャンクで読み取る

チャンクでデータを読み取ると、メモリ内のデータの一部にアクセスでき、データに前処理を適用して、生データではなく処理されたデータを保持できます。このオプションを最初のオプションであるdtypesと組み合わせると、はるかに良いでしょう。

そのプロセスのpandasクックブックセクションを指摘したいと思いますここで見つけることができます。そこで2つのセクションに注意してください。

オプション3:Dask

DaskはDaskのWebサイトで次のように定義されているフレームワークです。

Daskは分析に高度な並列処理を提供し、お気に入りのツールの大規模なパフォーマンスを実現します

パンダが届かない部分をカバーするために生まれました。Daskは強力なフレームワークであり、分散処理することで、より多くのデータアクセスが可能になります。

daskはデータ全体を前処理するために使用できます。Daskはチャンク部分を処理します。したがって、パンダとは異なり、処理ステップを定義してDaskに処理を任せることができます。Daskは、明示的に、computeおよび/またはによってプッシュされる前に計算を適用しませんpersist(違いについては、こちらの回答を参照してください)。

その他の援助(アイデア)

  • データ用に設計されたETLフロー。生データから必要なものだけを保持します。
    • まず、ETLをDaskやPySparkなどのフレームワークを使用してデータ全体に適用し、処理されたデータをエクスポートします。
    • 次に、処理されたデータが全体としてメモリに収まるかどうかを確認します。
  • RAMを増やすことを検討してください。
  • クラウドプラットフォームでそのデータを操作することを検討してください。

0

上記の回答に加えて、CSVを処理してからcsv、parquetまたはSQLにエクスポートする場合は、d6tstackが別の優れたオプションです。複数のファイルをロードでき、データスキーマの変更(列の追加/削除)を処理します。コアサポートのチャンクはすでに組み込まれています。

def apply(dfg):
    # do stuff
    return dfg

c = d6tstack.combine_csv.CombinerCSV([bigfile.csv], apply_after_read=apply, sep=',', chunksize=1e6)

# or
c = d6tstack.combine_csv.CombinerCSV(glob.glob('*.csv'), apply_after_read=apply, chunksize=1e6)

# output to various formats, automatically chunked to reduce memory consumption
c.to_csv_combine(filename='out.csv')
c.to_parquet_combine(filename='out.pq')
c.to_psql_combine('postgresql+psycopg2://usr:pwd@localhost/db', 'tablename') # fast for postgres
c.to_mysql_combine('mysql+mysqlconnector://usr:pwd@localhost/db', 'tablename') # fast for mysql
c.to_sql_combine('postgresql+psycopg2://usr:pwd@localhost/db', 'tablename') # slow but flexible

0

誰かがまだこのようなものを探している場合、modinと呼ばれるこの新しいライブラリが役立つことがわかりました。読み取りに役立つ分散コンピューティングを使用しています。ここにパンダとその機能を比較する素晴らしい記事があります。基本的にパンダと同じ機能を使用します。

import modin.pandas as pd
pd.read_csv(CSV_FILE_NAME)

この新しいモジュールmodinと定評のあるモジュールとの違いについてコメントしていただけますdask.dataframeか?たとえば、すべてのローカルCPUコアを利用するには、pandasからdaskへの移動をご覧ください。
jpp '12

0

@unutbuで言及されているように、チャンクのforループ内に記述したいプロセス関数を確認したい場合は、chunksizeオプションを使用する前に、単にnrowsオプションを使用できます。

small_df = pd.read_csv(filename, nrows=100)

プロセスブロックの準備ができていることを確認したら、データフレーム全体のチャンクforループに配置できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.