摘要
adataframe
是DataFrame
,行数为800k。当然,它消耗了一点内存。当我这样做时:
adataframe = adataframe.tail(144)
内存未发布。
你可能会认为已经发布,但它似乎已被使用,但它被标记为免费并且将被Python重用。但是,如果我尝试创建一个新的800k行DataFrame
并且只保留一小片,则内存使用量会增加。如果我再次这样做,它会再次增长,无限期。
我使用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
(在gc.collect()
之前添加print_memory_usage()
没有任何显着差异。)
我该怎么办?
答案 0 :(得分:1)
你可以说它已经发布了,但它似乎被使用了,但是它被标记为免费并且将被Python重用。
纠正maxrss
的工作原理(它测量峰值内存使用情况)。请参阅1。
那么问题就是为什么垃圾收集器在子集化之后不会清理原始的DataFrame。
我怀疑这是因为subsetting返回一个DataFrame作为原始代理的代理(因此不需要复制值)。这将导致相对快速的子集操作,但也会导致内存泄漏,例如您在设置值时发现的内容和here。
答案 1 :(得分:1)
正如@Alex所指出的那样,切片数据帧只会给你原始帧的视图,但不会删除它;您需要使用.copy()
。但是,即使我使用.copy()
,内存使用量也会增长,增长和增长,尽管速度较慢。
我怀疑这与Python
,numpy
和pandas
如何使用内存有关。数据帧不是内存中的单个对象;它包含指向其他对象的指针(特别是在这种特殊情况下,指向字符串,即“flags”列)。释放数据帧并释放这些对象后,可以将回收的可用内存空间分段。稍后,当创建一个巨大的新数据帧时,它可能无法使用碎片空间,并且可能需要分配新的空间。详细信息取决于许多小事情,例如Python
,numpy
和pandas
版本以及每个案例的详细信息。
我没有调查这些小细节,而是决定阅读一个庞大的时间序列,然后将其切片是不行的,而且我必须从头开始只阅读我需要的部分。我喜欢我为此创建的一些代码,即textbisect模块和FilePart类。