我有三个我试图连接的DataFrame.
concat_df = pd.concat([df1, df2, df3])
这会导致MemoryError.我该如何解决这个问题?
请注意,大多数现有的类似问题都是在读取大文件时发生的MemoryErrors上.我没有那个问题.我已将我的文件读入DataFrames.我只是不能连接那些数据.
我建议你通过连接将数据帧放入单个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中的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')
阅读后:
df = pd.read_csv('file.csv')
问题是,就像在其他答案中看到的那样,存在记忆问题.解决方案是将数据存储在磁盘上,然后构建一个独特的数据帧.
有了这么大的数据,性能就成了问题.
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
.
如果您想要提供有关数据结构和大小的更多信息,我可以编辑它.美丽的问题!
与@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)
有点猜测,但也许:
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中.正如您目前所做的那样,即使在连接它们之后,您仍然可以保留所有较小的数据帧.
尝试处理大型数据帧时,Dask可能是一个很好的选择 - 浏览Dask Docs