熊猫内存泄漏和数据帧排序

时间:2018-08-29 21:51:00

标签: python pandas

下面是一些代码,因此您可以准确地重现该问题。本质上,如果您不杀死内存,这将在几秒钟内将您的内存从90MB左右激增到5GB以上。随着内存的消耗,CPU也将受到限制。

排序功能退出后,内存也将保留。

如果我从一个大的主数据帧开始,将其切成薄片然后进行排序,我似乎只会浮出水面。如果我构建了一堆独立的数据框;这不会发生。

def test_sorting(df_list):
    counter = 0
    total = len(df_list)    
    for i in range(0,total):
        df_list[i].sort_index(inplace=True)

import pandas as pd
import numpy as np
from math import floor

def make_master_df(rows = 250000):
    groups = 5
    df = pd.DataFrame(np.random.randint(0,100,size=(rows, 26)), columns=list('ABCDEFGHIJKLMNOPQRSTUVWXYZ'))
    df["timestep"] = pd.Series([floor(x / groups) for x in range(0,rows)])
    df["id"] = pd.Series([ x % groups for x in range(0,rows)])
    df = df.set_index(["timestep", "id"]).sort_index()
    return df


def create_train_test_windows(df, train_size, test_size, slide_size, include_history = True, second_index=False):
    n = train_size + test_size
    size_multiplier = 1
    if(second_index):
        size_multiplier = df.index.levels[1].size
        n = n * size_multiplier
    list_df = None
    if(include_history):
        df.sort_index(ascending=True, inplace=True)
        list_df = [df[:-(i + n)] for i in range(0, df.shape[0], slide_size * size_multiplier)]
        list_df.insert(0,df[:])
        list_df = list_df[::-1]
    else:
        raise Exception("excluding history currently not supported.") 
    list_df = [x for x in list_df if x.shape[0] >= n]
    return list_df

master_df = make_master_df()
list_df = create_train_test_windows(master_df, 500, 20, 20, include_history=True, second_index=True)

这最终将在执行期间耗尽您的内存,并且执行结束后将保留该内存。

test_sorting(list_df)

注释:

我注意到每个切片的数据帧都为第一个索引(时间步长)保持完整的索引级别大小。

我已在每个步骤上强制使用gc.collect()只是为了尝试激进。 (根本不起作用)。

我已经在独立的python脚本和IPython笔记本中测试了相同的结果。

我最好的猜测是切片的数据帧实际上不是适当的切片;他们带来了很多行李,这在其他地方也有提及。

非常感谢任何见解/协助!

1 个答案:

答案 0 :(得分:1)

我解决了这个问题。

在上面发布的代码中,我使用以下代码创建数据框切片:

list_df = [df[:-(i + n)] for i in range(0, df.shape[0], slide_size * size_multiplier)]

这将返回对原始数据帧的引用,该引用将保留在原始数据帧上,而不是“真实”副本。因此,当我进行排序时,它将参考所需的原始数据帧以及内存消耗为何爆炸的方式来创建所有所需的索引。

要解决此问题,我现在使用以下内容将数据框切成薄片:

list_df = [df[:-(i + n)].copy() for i in range(0, df.shape[0], slide_size * size_multiplier)]

.copy()返回完整副本,不包含对原始数据帧的引用。

注意事项

使用.copy()选项,我获得了30GB的内存消耗,并且在排序期间峰值上升了30.3GB左右。我创建切片的执行时间要慢一些,但是我的排序速度却要快得多。

没有.copy()选项,我的起始容量约为95MB,结束时约为32GB。我的切片创建速度略快,而排序速度却指数级降低。它还引入了一个潜在的警告,即取决于我要对每个切片进行排序的方式以及切片重叠的事实,我可能正在做以前的工作。

摘要,如果您打算对较大数据帧的片进行任何奇特的工作,从性能的角度来看,使用内存和cpu角度复制这些片似乎更好。切片上的 .copy()运算符。

示例:

df[1:9].copy()