【问题标题】:Memory is not released when taking a small slice of a DataFrame获取一小部分 DataFrame 时不会释放内存
【发布时间】:2016-11-05 08:56:37
【问题描述】:

总结

adataframe 是一个有 800k 行的 DataFrame。自然,它会消耗一些内存。当我这样做时:

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

(在print_memory_usage() 之前添加gc.collect() 不会产生任何显着差异。)

我该怎么办?

【问题讨论】:

    标签: pandas


    【解决方案1】:

    您可以争辩说它已发布,但它似乎已被使用,但它被标记为免费并且将被 Python 重用。

    这就是maxrss 的工作原理(它测量峰值内存使用量)。见here

    那么问题是为什么垃圾收集器在原始数据帧被子集化后没有清理它们。

    我怀疑这是因为子集返回一个 DataFrame,它充当原始数据的代理(因此不需要复制值)。这将导致相对较快的子集操作,但也会导致内存泄漏,就像您在设置值时发现的那样和weird speed characteristics

    【讨论】:

    • 确实,如果我将result = result.tail(144) 更改为result = result.tail(144).copy(); gc.collect(),10 次迭代后它会减少大约 100M。然而,它仍在增长、增长和增长,尽管速度较低。
    【解决方案2】:

    正如@Alex 所指出的,对数据帧进行切片只会让您看到原始帧,但不会删除它;您需要为此使用.copy()。然而,即使我使用.copy(),内存使用量也在不断增长,尽管速度较慢。

    我怀疑这与Pythonnumpypandas 使用内存的方式有关。数据框不是内存中的单个对象;它包含指向其他对象的指针(特别是在这种特殊情况下,指向字符串,即“标志”列)。当数据帧被释放,并且这些对象被释放时,回收的空闲内存空间可以被碎片化。稍后,当创建一个巨大的新数据帧时,它可能无法使用碎片空间,并且可能需要分配新空间。细节取决于很多小东西,例如Pythonnumpypandas 版本,以及每个案例的细节。

    与其研究这些小细节,我决定阅读大量时间序列然后对其进行切片是不行的,我必须从一开始就只阅读我需要的部分。我喜欢我为此创建的一些代码,即textbisect 模块和FilePart 类。

    【讨论】:

      猜你喜欢
      • 2012-06-12
      • 1970-01-01
      • 2016-08-19
      • 2014-04-02
      • 2014-11-17
      • 1970-01-01
      • 1970-01-01
      • 2020-11-22
      相关资源
      最近更新 更多