web-dev-qa-db-ja.com

MemoryErrorを実行せずに複数のpandas.DataFramesを連結する方法

連結しようとしている3つのDataFrameがあります。

concat_df = pd.concat([df1, df2, df3])

これにより、MemoryErrorが発生します。どうすれば解決できますか?

既存の同様の質問のほとんどは、大きなファイルを読み取るときに発生するMemoryErrorに関するものです。私にはその問題はありません。ファイルをDataFramesに読み込みました。そのデータを連結することはできません。

17
bluprince13

コミュニティの回答に感謝します。しかし、私の場合、問題は実際には32ビットPythonを使用していたためであることがわかりました。

メモリ制限 Windows 32および64ビットOS用に定義されています。 32ビットプロセスの場合、2 GBのみです。したがって、たとえRAMが2GBを超えていて、64ビットOSを実行しているが、32ビットプロセスを実行している場合でも、そのプロセスは2つに制限されますGB of RAM-私の場合、そのプロセスはPythonでした。

64ビットPythonにアップグレードしましたが、それ以来メモリエラーは発生していません。

その他の関連する質問は次のとおりです。 64ビットウィンドウでのPython 32ビットメモリ制限Python 32ビットまたはPython = 64ビットなぜこのnumpy配列が大きすぎてロードできないのですか?

3
bluprince13

連結によってデータフレームを単一のcsvファイルに配置することをお勧めします。次に、csvファイルを読み取ります。

それを実行します:

# write df1 content in file.csv
df1.to_csv('file.csv', index=False)
# append df2 content to file.csv
df2.to_csv('file.csv', mode='a', columns=False, index=False)
# append df3 content to file.csv
df3.to_csv('file.csv', mode='a', columns=False, index=False)

# free memory
del df1, df2, df3

# read all df1, df2, df3 contents
df = pd.read_csv('file.csv')

このソリューションが十分なパフォーマンスを発揮しない場合、通常よりも大きなファイルを連結します。行う:

df1.to_csv('file.csv', index=False)
df2.to_csv('file1.csv', index=False)
df3.to_csv('file2.csv', index=False)

del df1, df2, df3

次に、bashコマンドを実行します。

cat file1.csv >> file.csv
cat file2.csv >> file.csv
cat file3.csv >> file.csv

またはpythonでcsvファイルを連結:

def concat(file1, file2):
    with open(file2, 'r') as filename2:
        data = file2.read()
    with open(file1, 'a') as filename1:
        file.write(data)

concat('file.csv', 'file1.csv')
concat('file.csv', 'file2.csv')
concat('file.csv', 'file3.csv')

読んだ後:

df = pd.read_csv('file.csv')
17
glegoux

問題は、他の回答で見られるように、記憶の問題です。そして解決策は、データをディスクに保存し、一意のデータフレームを構築することです。

このような膨大なデータでは、パフォーマンスが問題になります。

テキストモードでの変換が発生するため、csvソリューションは非常に低速です。 HDF5ソリューションは、バイナリモードを使用しているため、短く、エレガントで、高速です。 pickle を使用して、バイナリモードで3番目の方法を提案します。これはさらに高速であるように見えますが、より技術的であり、さらにスペースが必要です。そして、4番目は手作業です。

ここにコード:

import numpy as np
import pandas as pd

# a DataFrame factory:
dfs=[]
for i in range(10):
    dfs.append(pd.DataFrame(np.empty((10**5,4)),columns=range(4)))

# a csv solution
def bycsv(dfs):
    md,hd='w',True
    for df in dfs:
        df.to_csv('df_all.csv',mode=md,header=hd,index=None)
        md,hd='a',False
    #del dfs
    df_all=pd.read_csv('df_all.csv',index_col=None)
    os.remove('df_all.csv') 
    return df_all    

より良いソリューション:

def byHDF(dfs):
    store=pd.HDFStore('df_all.h5')
    for df in dfs:
        store.append('df',df,data_columns=list('0123'))
    #del dfs
    df=store.select('df')
    store.close()
    os.remove('df_all.h5')
    return df

def bypickle(dfs):
    c=[]
    with open('df_all.pkl','ab') as f:
        for df in dfs:
            pickle.dump(df,f)
            c.append(len(df))    
    #del dfs
    with open('df_all.pkl','rb') as f:
        df_all=pickle.load(f)
        offset=len(df_all)
        df_all=df_all.append(pd.DataFrame(np.empty(sum(c[1:])*4).reshape(-1,4)))

        for size in c[1:]:
            df=pickle.load(f)
            df_all.iloc[offset:offset+size]=df.values 
            offset+=size
    os.remove('df_all.pkl')
    return df_all

同種のデータフレームの場合、さらに改善することができます。

def byhand(dfs):
    mtot=0
    with open('df_all.bin','wb') as f:
        for df in dfs:
            m,n =df.shape
            mtot += m
            f.write(df.values.tobytes())
            typ=df.values.dtype                
    #del dfs
    with open('df_all.bin','rb') as f:
        buffer=f.read()
        data=np.frombuffer(buffer,dtype=typ).reshape(mtot,n)
        df_all=pd.DataFrame(data=data,columns=list(range(n))) 
    os.remove('df_all.bin')
    return df_all

また、パフォーマンスを比較するために(わずか32 Mb)データをテストします。 4 Gbの場合、約128を掛ける必要があります。

In [92]: %time w=bycsv(dfs)
Wall time: 8.06 s

In [93]: %time x=byHDF(dfs)
Wall time: 547 ms

In [94]: %time v=bypickle(dfs)
Wall time: 219 ms

In [95]: %time y=byhand(dfs)
Wall time: 109 ms

チェック :

In [195]: (x.values==w.values).all()
Out[195]: True

In [196]: (x.values==v.values).all()
Out[196]: True

In [197]: (x.values==y.values).all()
Out[196]: True

もちろん、すべてを改善し、問題に合わせて調整する必要があります。

たとえば、df3をサイズ 'total_memory_size-df_total_size'のチャンクに分割して、bypickleを実行できます。

必要に応じてデータ構造とサイズに関する詳細情報を提供していただければ、編集できます。美しい質問!

14
B. M.

@glegouxが示唆するものと同様に、pd.DataFrame.to_csvは追加モードで書き込むことができるため、次のようなことができます。

df1.to_csv(filename)
df2.to_csv(filename, mode='a', columns=False)
df3.to_csv(filename, mode='a', columns=False)

del df1, df2, df3
df_concat = pd.read_csv(filename)
8
Pietro Tortella

ちょっとここで推測しますが、多分:

df1 = pd.concat([df1,df2])
del df2
df1 = pd.concat([df1,df3])
del df3

明らかに、ループとしてそれ以上を行うことができますが、重要なことは、df2、df3などを削除することです。質問でそれをしているとき、古いデータフレームをクリアすることはないので、必要なメモリの約2倍のメモリを使用しています。

より一般的に、あなたが読んで連結している場合、私はこのようなことをします(3つのCSVがある場合:foo0、foo1、foo2):

concat_df = pd.DataFrame()
for i in range(3):
    temp_df = pd.read_csv('foo'+str(i)+'.csv')
    concat_df = pd.concat( [concat_df, temp_df] )

つまり、ファイルを読み込んでいるときに、小さなデータフレームをメモリに一時的に保持するだけで、それらを結合されたdf、concat_dfに連結します。現在は、連結した後でも、すべての小さいデータフレームを保持しています。

7
JohnE

Daskは、大きなデータフレームを処理するのに適したオプションです。 Dask Docs を通過します。

5
Tanu

個々のデータフレームをHDF Store に保存し、1つの大きなデータフレームのようにストアを呼び出すことができます。

# name of store
fname = 'my_store'

with pd.get_store(fname) as store:

    # save individual dfs to store
    for df in [df1, df2, df3, df_foo]:
        store.append('df',df,data_columns=['FOO','BAR','ETC']) # data_columns = identify the column in the dfs you are appending

    # access the store as a single df
    df = store.select('df', where = ['A>2'])  # change where condition as required (see documentation for examples)
    # Do other stuff with df #

# close the store when you're done
os.remove(fname)
3
NickBraunagel

別のオプション:

1)_df1_を.csvファイルに書き込みます:df1.to_csv('Big file.csv')

2).csvファイルを開き、_df2_を追加します。

_with open('Big File.csv','a') as f:
    df2.to_csv(f, header=False)
_

3)_df3_でステップ2を繰り返します

_with open('Big File.csv','a') as f:
    df3.to_csv(f, header=False)
_
2
Walt Reed

多数のDataFrameを「成長中の」DataFrameに連結しようとしたときに、同様のパフォーマンスの問題が発生しました。

私の回避策は、すべてのサブDataFramesをリストに追加し、サブDataFramesの処理が完了したらDataFramesのリストを連結することでした。これにより、ランタイムがほぼ半分になります。

0
Prakhar Agarwal