Pandas 获取数据帧的一小部分时,不会释放内存
摘要Pandas 获取数据帧的一小部分时,不会释放内存,pandas,Pandas,摘要 adataframe是一个具有800k行的DataFrame。当然,它会消耗一点内存。当我这样做时: adataframe = adataframe.tail(144) 内存没有释放 您可能会争辩说它已发布,但似乎已被使用,但它被标记为免费,将被Python重用。但是,如果我尝试创建一个新的800k行数据帧,并且只保留一个小片段,那么内存使用就会增加。如果我再做一次,它就会无限地再次生长 我正在使用Debian Jessie的Python 3.4.2和Pandas 0.18.1和nump
adataframe
是一个具有800k行的DataFrame
。当然,它会消耗一点内存。当我这样做时:
adataframe = adataframe.tail(144)
内存没有释放
您可能会争辩说它已发布,但似乎已被使用,但它被标记为免费,将被Python重用。但是,如果我尝试创建一个新的800k行数据帧
,并且只保留一个小片段,那么内存使用就会增加。如果我再做一次,它就会无限地再次生长
我正在使用Debian Jessie的Python 3.4.2和Pandas 0.18.1和numpy 1.11.1
用最少的程序演示
使用下面的程序,我创建了一个字典
data = {
0: a_DataFrame_loaded_from_a_CSV,_only_the_last_144_rows,
1: same_thing,
# ...
9: same_thing,
}
在创建字典时,我会监控内存使用情况。这是:
#!/usr/bin/env python3
from resource import getrusage, RUSAGE_SELF
import pandas as pd
def print_memory_usage():
print(getrusage(RUSAGE_SELF).ru_maxrss)
def read_dataframe_from_csv(f):
result = pd.read_csv(f, parse_dates=[0],
names=('date', 'value', 'flags'),
usecols=('date', 'value', 'flags'),
index_col=0, header=None,
converters={'flags': lambda x: x})
result = result.tail(144)
return result
print_memory_usage()
data = {}
for i in range(10):
with open('data.csv') as f:
data[i] = read_dataframe_from_csv(f)
print_memory_usage()
结果
如果data.csv
仅包含几行(例如144行,在这种情况下,切片是冗余的),内存使用量增长非常缓慢。但如果data.csv
包含800k行,则结果与以下类似:
52968
153388
178972
199760
225312
244620
263656
288300
309436
330568
349660
(在print\u memory\u usage()
之前添加gc.collect()
,不会产生任何显著差异。)
我能做些什么?
您可能会争辩说它已发布,但似乎已被使用,但它被标记为免费,将被Python重用
请更正maxrss
的工作原理(它测量内存使用峰值)。看
因此,接下来的问题是,为什么垃圾收集器在原始数据帧被子集后不清理它们
我怀疑这是因为子集将返回一个充当原始数据帧代理的数据帧(因此不需要复制值)。这将导致相对较快的子集操作,但也会导致内存泄漏,就像您在设置值时发现的那样
您可能会争辩说它已发布,但似乎已被使用,但它被标记为免费,将被Python重用
请更正maxrss
的工作原理(它测量内存使用峰值)。看
因此,接下来的问题是,为什么垃圾收集器在原始数据帧被子集后不清理它们
我怀疑这是因为子集将返回一个充当原始数据帧代理的数据帧(因此不需要复制值)。这将导致相对快速的子集操作,但也会导致内存泄漏,就像您在设置值时发现的那样。正如@Alex所指出的,对数据帧进行切片只能查看原始帧,但不会删除它;为此,您需要使用
.copy()
。然而,即使在我使用.copy()
时,内存使用量也在不断增长,尽管速度较慢
我怀疑这与Python
、numpy
和pandas
如何使用内存有关。数据帧不是内存中的单个对象;它包含指向其他对象的指针(特别是在本例中,指向字符串,即“flags”列)。当数据帧被释放,并且这些对象被释放时,回收的可用内存空间可以被碎片化。稍后,当创建一个巨大的新数据帧时,它可能无法使用碎片空间,可能需要分配新的空间。细节取决于许多小东西,例如Python
、numpy
和pandas
版本,以及每个案例的细节
我没有研究这些小细节,而是决定阅读一个庞大的时间序列,然后对其进行切片是不可能的,而且我必须从一开始就只阅读我需要的部分。我喜欢我为此创建的一些代码,即模块和类。正如@Alex所指出的,对数据帧进行切片只能查看原始帧,但不能删除它;为此,您需要使用
.copy()
。然而,即使在我使用.copy()
时,内存使用量也在不断增长,尽管速度较慢
我怀疑这与Python
、numpy
和pandas
如何使用内存有关。数据帧不是内存中的单个对象;它包含指向其他对象的指针(特别是在本例中,指向字符串,即“flags”列)。当数据帧被释放,并且这些对象被释放时,回收的可用内存空间可以被碎片化。稍后,当创建一个巨大的新数据帧时,它可能无法使用碎片空间,可能需要分配新的空间。细节取决于许多小东西,例如Python
、numpy
和pandas
版本,以及每个案例的细节
我没有研究这些小细节,而是决定阅读一个庞大的时间序列,然后对其进行切片是不可能的,而且我必须从一开始就只阅读我需要的部分。我喜欢为此创建的一些代码,即模块和类。事实上,如果我将
result=result.tail(144)
更改为result=result.tail(144).copy();gc.collect()
,经过10次迭代后,它的消耗量减少了约1亿。然而,它仍然在不断地增长,尽管增长速度较低。事实上,如果我将result=result.tail(144)
更改为result=result.tail(144.copy();gc.collect()
,经过10次迭代后,它的消耗量减少了约1亿。然而,尽管增长率较低,它仍然在不断增长。