Оптимизация добавления значения в массив
Ситуация следующая: допустим, у нас есть цикл, в котором на каждой итерации происходит какое-то вычисление значения. И полученное значение мы добавляем в массив на каждой итерации.
Например что-то вроде такого :
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:
Нам нужно получить с этих данные отношение суммы платежей всех игроков, чей 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 шт):
На больших данных лучше всего использовать векторные вычисления. Для этого есть прекрасная библиотека 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 раз выше, в зависимости от размера. Сейчас же будет еще быстрее.
