如何连接多个pandas.DataFrames而不会遇到MemoryError

blu*_*e13 21 python memory memory-management pandas

我有三个我试图连接的DataFrame.

concat_df = pd.concat([df1, df2, df3])
Run Code Online (Sandbox Code Playgroud)

这会导致MemoryError.我该如何解决这个问题?

请注意,大多数现有的类似问题都是在读取大文件时发生的MemoryErrors上.我没有那个问题.我已将我的文件读入DataFrames.我只是不能连接那些数据.

gle*_*oux 19

我建议你通过连接将数据帧放入单个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')
Run Code Online (Sandbox Code Playgroud)

如果这个解决方案不符合性能要求,那么要连接比通常更大的文件.做:

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
Run Code Online (Sandbox Code Playgroud)

然后运行bash命令:

cat file1.csv >> file.csv
cat file2.csv >> file.csv
cat file3.csv >> file.csv
Run Code Online (Sandbox Code Playgroud)

或者python中的concat 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')
Run Code Online (Sandbox Code Playgroud)

阅读后:

df = pd.read_csv('file.csv')
Run Code Online (Sandbox Code Playgroud)

  • 但是,如果我们要沿着“ axis = 1”列进行连接,那么您的答案将无效! (2认同)
  • 目前你应该使用 `header=False` 而不是 `columns=False` (2认同)

B. *_* M. 17

问题是,就像在其他答案中看到的那样,存在记忆问题.解决方案是将数据存储在磁盘上,然后构建一个独特的数据帧.

有了这么大的数据,性能就成了问题.

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    
Run Code Online (Sandbox Code Playgroud)

更好的方案:

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
Run Code Online (Sandbox Code Playgroud)

对于同构数据帧,我们可以做得更好:

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
Run Code Online (Sandbox Code Playgroud)

并对一些(少量,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
Run Code Online (Sandbox Code Playgroud)

检查:

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
Run Code Online (Sandbox Code Playgroud)

当然,所有这一切都必须改进和调整,以适应您的问题.

例如,df3可以在大小为'total_memory_size - df_total_size'的块中拆分,以便能够运行bypickle.

如果您想要提供有关数据结构和大小的更多信息,我可以编辑它.美丽的问题!

  • 我尝试使用解决方案“手动”并收到错误:“无法从内存缓冲区创建对象数组”。我不确定它是否可以在 Python3 中修复。 (2认同)
  • 除了“bycsv”之外,这些似乎都不适用于最新版本的库。 (2认同)

Pie*_*lla 8

与@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)
Run Code Online (Sandbox Code Playgroud)


Joh*_*hnE 8

有点猜测,但也许:

df1 = pd.concat([df1,df2])
del df2
df1 = pd.concat([df1,df3])
del df3
Run Code Online (Sandbox Code Playgroud)

显然,你可以做更多的循环,但关键是你要删除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] )
Run Code Online (Sandbox Code Playgroud)

换句话说,当您正在读取文件时,您只是暂时将小数据帧保留在内存中,直到将它们连接到组合的df,concat_df中.正如您目前所做的那样,即使在连接它们之后,您仍然可以保留所有较小的数据帧.


Tan*_*anu 5

尝试处理大型数据帧时,Dask可能是一个很好的选择 - 浏览Dask Docs


blu*_*e13 4

我感谢社区的回答。然而,就我而言,我发现问题实际上是由于我使用的是 32 位 Python。

Windows 32 和 64 位操作系统定义了内存限制。对于 32 位进程来说,只有 2 GB。因此,即使您的 RAM 超过 2GB,并且即使您运行的是 64 位操作系统,但运行的是 32 位进程,那么该进程将仅限于 2 GB RAM - 在我的情况下该进程是Python。

我升级到了 64 位 Python,从那以后就再也没有出现过内存错误!

其他相关问题有:64位Windows上的Python 32位内存限制我应该使用Python 32位还是Python 64位为什么这个numpy数组太大而无法加载?