簡體   English   中英

如何在不遇到 MemoryError 的情況下連接多個 pandas.DataFrames

[英]How to concatenate multiple pandas.DataFrames without running into MemoryError

我有三個要連接的 DataFrame。

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

這會導致 MemoryError。 我該如何解決這個問題?

請注意,大多數現有的類似問題都是關於讀取大文件時發生的 MemoryErrors。 我沒有那個問題。 我已將我的文件讀入 DataFrames。 我只是無法連接這些數據。

問題是,就像在其他答案中看到的那樣,是記憶問題。 一個解決方案是將數據存儲在磁盤上,然后構建一個唯一的數據幀。

對於如此龐大的數據,性能是一個問題。

csv 解決方案非常慢,因為會在文本模式下進行轉換。 由於使用二進制模式,HDF5 解決方案更短、更優雅、更快。 我在二進制模式中提出了第三種方式,使用pickle ,它似乎更快,但更具技術性並且需要更多空間。 第四個,手動。

這里的代碼:

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

如果你願意的話,如果你提供更多關於你的數據結構和大小的信息,我可以編輯它。 漂亮的問題!

我建議您通過串聯將數據幀放入單個 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')

有點猜測這里,但也許:

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

顯然,您可以循環執行更多操作,但關鍵是您要隨時刪除 df2、df3 等。 當您在問題中這樣做時,您永遠不會清除舊的數據幀,因此您使用的內存大約是您需要的兩倍。

更一般地說,如果您正在閱讀和連接,我會這樣做(如果您有 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。 正如您目前所做的那樣,您將保留所有較小的數據幀,即使在連接它們之后也是如此。

類似@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)

Dask 可能是嘗試處理大型數據幀的不錯選擇 - 瀏覽Dask Docs

您可以將您的單個數據幀存儲在 HDF Store 中,然后像調用一個大數據幀一樣調用該存儲。

# 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)

我感謝社區的回答。 但是,就我而言,我發現問題實際上是由於我使用的是 32 位 Python。

為 Windows 32 和 64 位操作系統定義了內存限制 對於 32 位進程,它只有 2 GB。 因此,即使您的 RAM 超過 2GB,即使您運行的是 64 位操作系統,但您運行的是 32 位進程,那么該進程也將僅限於 2 GB 的 RAM - 在我的情況下,該進程是 Python。

我升級到 64 位 Python,從那時起就沒有出現內存錯誤!

其他相關問題是: Python 32-bit memory limits on 64bit windows我應該使用 Python 32bit 還是 Python 64bit為什么這個 numpy 數組太大而無法加載?

另一種選擇:

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)

我在嘗試將大量 DataFrame 連接到“不斷增長”的 DataFrame 時遇到了類似的性能問題。

我的解決方法是將所有子數據幀附加到一個列表中,然后在子數據幀的處理完成后連接數據幀列表。 這將使運行時間幾乎減少一半。

寫入硬盤時, df.to_csv會為columns=False引發錯誤。

以下解決方案工作正常:

# write df1 to hard disk as file.csv
train1.to_csv('file.csv', index=False)
# append df2 to file.csv
train2.to_csv('file.csv', mode='a', header=False, index=False)
# read the appended csv as df
train = pd.read_csv('file.csv')

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM