Оптимизация добавления значения в массив

Ситуация следующая: допустим, у нас есть цикл, в котором на каждой итерации происходит какое-то вычисление значения. И полученное значение мы добавляем в массив на каждой итерации.

Например что-то вроде такого :

array = []

for i in range(1, days):
    array.append((data.query('feature_a >= @i & feature_b <= @i')['number'].sum() /\
                  data.query('feature_a >= @i & feature_b < @i')['number'].sum()))

И все работает, но проблема в следующем. В данной ситуации при увеличении days, у нас будет с каждой итерацией медленней работать код. Я так понимаю это происходит из-за постоянного добавления значения в массив. Каюсь, теоретически я не совсем понимаю почему так. Но знаю это эмпирически.

По этому вопрос следующий: есть ли возможно этого избежать, если да то какая. И отдельное спасибо если будет теоретическое объяснение почему это происходит.

Практическая задача для понимания что хочется получить и что сделать:

У нас есть примерно такой вот DataFrame:

Пример DataFrame'a

Нам нужно получить с этих данные отношение суммы платежей всех игроков, чей lifetime не меньше N, а paydar не больше N, к сумме платежей всех игроков, чей lifetime не меньше N а paydar меньше N.

В целом это получается что-то вроде метрики: SumRev_N / SumRev_N-1

Это можно получить кодом выше, заменив необходимые наименование. То есть

array = []

for i in range(1, days):
    array.append((data.query('lifetime >= @i & paydar <= @i')['payment'].sum() /\
                  data.query('lifetime >= @i & paydar < @i')['payment'].sum()))

Ответы (1 шт):

Автор решения: D.Vinogradov

На больших данных лучше всего использовать векторные вычисления. Для этого есть прекрасная библиотека NumPy

Вообще я бы изначально, зная границы цикла, отсекал бы по ним часть датафрейма. (в этом коде ваш изначальный датафрейм data - это df2)

low, hight = 1, 300 #границы цикла

Отсекаем df по условию. Для отсечения достаточно включить одно ваше условие, потому что второе в него входит.

def condit(x, y):
    df_of = df2[((df2.lifetime >= x) | (df2.paydar <= x)) & ((df2.lifetime >= y) & (df2.paydar <= y))]
    return df_of

Отсеченный фрейм:

df = condit(low, hight)

Создадим функцию вашего изначального цикла (как оказалось, без него не обойтись :) )

def div_elems(x):
    increasing = np.sum(np.where((df['lifetime'].values >= x) & (df['paydar'].values <= x), df['payment'].values, 0))
    strictly = np.sum(np.where((df['lifetime'].values >= x) & (df['paydar'].values < x), df['payment'].values, 0))
    return np.true_divide(increasing, strictly)

Получаем ваш список:

your_list = [div_elems(i) for i in np.arange(low, hight)]

P.S. Изначально прикидывал без отсечения, скорость с NumPy, в отличии от Вашего цикла была от 2 до 4 раз выше, в зависимости от размера. Сейчас же будет еще быстрее.

→ Ссылка