Утечки памяти Pandas и сортировка кадров данных - PullRequest
0 голосов
/ 30 августа 2018

Ниже приведен код, позволяющий точно воспроизвести проблему. По сути это приведет к увеличению вашей памяти с 90 МБ или около того до 5 ГБ за считанные секунды, если вы не убьете ее. С потреблением памяти также придет ограниченный процессор.

Память также будет сохраняться после выхода из функции сортировки.

Похоже, что эта проблема появляется только в том случае, если я начинаю с большого основного кадра данных, делаю его на части и затем выполняю сортировку. Если я построю кучу независимых фреймов данных; этого не происходит.

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 голосов
/ 30 августа 2018

Я решил это.

В приведенном выше коде я использую следующее для создания фрагментов данных:

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 () я получаю 30 ГБ памяти, а во время сортировки скачок возрастает до 30,3 ГБ или около того. Время выполнения создания фрагментов немного медленнее, но скорость сортировки значительно выше.

Без опции .copy () я начинаю с 95 МБ и заканчиваю около 32 ГБ. Мое создание среза незначительно быстрее, в то время как моя сортировка экспоненциально медленнее. Это также вводит потенциальное предостережение в том, что в зависимости от того, как я хочу отсортировать каждый срез и того факта, что мои срезы перекрываются, я могу не выполнять работу, которую выполнял ранее.

Сводка Если вы намереваетесь выполнять какую-либо сложную работу со срезами большего фрейма данных, с точки зрения производительности, как представляется, гораздо лучше копировать эти срезы как с точки зрения памяти, так и с точки зрения процессора, используя .copy () оператор на срезе.

пример:

df[1:9].copy()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...