所以我一直在尝试使用pandas read_csv
函数读取内存中的3.2GB文件,但是我继续遇到某种内存泄漏,我的内存使用率会高峰90%+
。
作为替代品
我尝试定义dtype
以避免将内存中的数据保留为字符串,但看到了类似的行为。
尝试了numpy读取csv,认为我会得到一些不同的结果但是肯定是错误的。
逐行尝试阅读遇到了同样的问题,但确实很慢。
我最近搬到了python 3,所以认为可能会有一些bug,但在python2 + pandas上看到了类似的结果。
有问题的文件是kaggle competition grupo bimbo
中的train.csv文件系统信息:
RAM: 16GB, Processor: i7 8cores
如果您想了解其他任何内容,请与我们联系。
谢谢:)
编辑1:内存飙升!不是泄漏(对不起,我的坏。)编辑2:csv文件的样本
Semana,Agencia_ID,Canal_ID,Ruta_SAK,Cliente_ID,Producto_ID,Venta_uni_hoy,Venta_hoy,Dev_uni_proxima,Dev_proxima,Demanda_uni_equil
3,1110,7,3301,15766,1212,3,25.14,0,0.0,3
3,1110,7,3301,15766,1216,4,33.52,0,0.0,4
3,1110,7,3301,15766,1238,4,39.32,0,0.0,4
3,1110,7,3301,15766,1240,4,33.52,0,0.0,4
3,1110,7,3301,15766,1242,3,22.92,0,0.0,3
编辑3:文件 74180465
中的行数其他一个简单的pd.read_csv('filename', low_memory=False)
我试过了
from numpy import genfromtxt
my_data = genfromtxt('data/train.csv', delimiter=',')
更新 下面的代码工作正常,但我仍然想要解决这个问题的根源,一定有什么问题。
import pandas as pd
import gc
data = pd.DataFrame()
data_iterator = pd.read_csv('data/train.csv', chunksize=100000)
for sub_data in data_iterator:
data.append(sub_data)
gc.collect()
编辑:一段有效的代码。 感谢所有帮助人员,我通过添加python dtypes而不是numpy来搞砸了我的dtypes。一旦我修复了下面的代码就像一个魅力。
dtypes = {'Semana': pd.np.int8,
'Agencia_ID':pd.np.int8,
'Canal_ID':pd.np.int8,
'Ruta_SAK':pd.np.int8,
'Cliente_ID':pd.np.int8,
'Producto_ID':pd.np.int8,
'Venta_uni_hoy':pd.np.int8,
'Venta_hoy':pd.np.float16,
'Dev_uni_proxima':pd.np.int8,
'Dev_proxima':pd.np.float16,
'Demanda_uni_equil':pd.np.int8}
data = pd.read_csv('data/train.csv', dtype=dtypes)
这将内存消耗降至4Gb以下
答案 0 :(得分:1)
作为文本存储在存储器中的文件不像压缩二进制格式那样紧凑,但是它在数据方面相对紧凑。如果它是一个简单的ascii文件,除了任何文件头信息外,每个字符只有1个字节。 Python字符串具有类似的关系,其中内部python内容有一些开销,但每个额外的字符只添加1个字节(来自__sizeof__
的测试)。一旦开始转换为数字类型和集合(列表,数组,数据框等),开销就会增加。例如,列表必须存储每个位置的类型和值,而字符串仅存储值。
>>> s = '3,1110,7,3301,15766,1212,3,25.14,0,0.0,3\r\n'
>>> l = [3,1110,7,3301,15766,1212,3,25.14,0,0.0,3]
>>> s.__sizeof__()
75
>>> l.__sizeof__()
128
进行一些测试(假设__sizeof__
是准确的):
import numpy as np
import pandas as pd
s = '1,2,3,4,5,6,7,8,9,10'
print ('string: '+str(s.__sizeof__())+'\n')
l = [1,2,3,4,5,6,7,8,9,10]
print ('list: '+str(l.__sizeof__())+'\n')
a = np.array([1,2,3,4,5,6,7,8,9,10])
print ('array: '+str(a.__sizeof__())+'\n')
b = np.array([1,2,3,4,5,6,7,8,9,10], dtype=np.dtype('u1'))
print ('byte array: '+str(b.__sizeof__())+'\n')
df = pd.DataFrame([1,2,3,4,5,6,7,8,9,10])
print ('dataframe: '+str(df.__sizeof__())+'\n')
返回:
string: 53
list: 120
array: 136
byte array: 106
dataframe: 152
答案 1 :(得分:1)
根据您的第二个图表,看起来好像您的机器会分配额外的4.368 GB内存,这大约相当于您的3.2 GB数据集的大小(假设1 GB开销,这可能是一段时间) )。
我试图找到一个可能发生这种情况的地方并且没有超级成功。但是,如果你有动力,也许你可以找到它。这是我走的路径:
This line读到:
def read(self, nrows=None):
if nrows is not None:
if self.options.get('skip_footer'):
raise ValueError('skip_footer not supported for iteration')
ret = self._engine.read(nrows)
此处,_engine
引用PythonParser
。
反过来,调用_get_lines()
。
调用数据source
。
它看起来像是从相对标准的字符串形式读取(请参阅here),如TextIOWrapper。
所以事情正在以标准文本的形式被读入并转换,这解释了缓慢的斜坡。
穗怎么样?我认为这是由these lines解释的:
ret = self._engine.read(nrows)
if self.options.get('as_recarray'):
return ret
# May alter columns / col_dict
index, columns, col_dict = self._create_index(ret)
df = DataFrame(col_dict, columns=columns, index=index)
ret
成为数据框的所有组成部分。
self._create_index()
将这些组件分开ret
:
def _create_index(self, ret):
index, columns, col_dict = ret
return index, columns, col_dict
到目前为止,所有内容都可以通过引用完成,并且对DataFrame()
的调用会继续这种趋势(请参阅here)。
所以,如果我的理论是正确的,DataFrame()
要么在某处复制数据,要么_engine.read()
在我已经确定的路径上的某个地方复制。