我有三个我正在尝试连接的DataFrame。
concat_df = pd.concat([df1, df2, df3])
这会导致MemoryError。我该如何解决这个问题?
请注意,大多数现有的类似问题都是在读取大文件时发生的MemoryErrors上。我没有那个问题。我已将我的文件读入DataFrames。我无法连接这些数据。
答案 0 :(得分:16)
我建议您通过连接将数据帧放入单个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')
答案 1 :(得分: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)
答案 2 :(得分:8)
问题是,就像在其他答案中看到的那样,存在记忆问题。解决方案是将数据存储在磁盘上,然后构建一个独特的数据帧。
如此庞大的数据,性能就成了问题。
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
。
如果您想要提供有关数据结构和大小的更多信息,我可以编辑它。美丽的问题!
答案 3 :(得分:7)
有点在这里猜测,但也许:
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中。正如您目前所做的那样,即使在连接它们之后,您仍然会保留所有较小的数据帧。
答案 4 :(得分:5)
Dask可能是尝试处理大型数据帧的不错选择 - 浏览Dask Docs
答案 5 :(得分:3)
您可以将个人数据框存储在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)
答案 6 :(得分:3)
我很感谢社区的回答。但是,在我的情况下,我发现问题实际上是由于我使用的是32位Python。
为Windows 32和64位操作系统定义了memory limits。对于32位进程,它只有2 GB。因此,即使您的RAM超过2GB,即使您运行的是64位操作系统,但是您运行的是32位进程,那么该进程将仅限于2 GB的RAM - 在我的情况下,该进程是Python。
我升级到64位Python,从那以后没有出现内存错误!
其他相关问题包括:Python 32-bit memory limits on 64bit windows,Should I use Python 32bit or Python 64bit,Why is this numpy array too big to load?
答案 7 :(得分:2)
另一种选择:
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
with open('Big File.csv','a') as f:
df3.to_csv(f, header=False)
答案 8 :(得分:0)
在尝试将大量DataFrame连接到“不断增长的”DataFrame时,我遇到了类似的性能问题。
我的解决方法是将所有子DataFrame追加到列表中,然后在完成子DataFrames的处理后连接DataFrames列表。这将使运行时间几乎减半。
答案 9 :(得分:0)
在写入硬盘时,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')