#python #pandas #numpy #optimization #vectorization
#python #pandas #numpy #оптимизация #векторизация
Вопрос:
Я работаю над чем-то, где время выполнения чрезвычайно важно, а данные, с которыми мы работаем, велики, но в основном проблема сводится к оптимизации решения для серии x, где x1 известно и x = ax b из предыдущей строки. Так, например, начальное состояние:
a b x
1 2 3
3 1
2 2
4 8
1 9
и конечное состояние будет выглядеть следующим образом:
a b x
1 2 3
3 1 5
2 2 16
4 8 72
1 9 81
потому что 3*1 2 = 5, 5*3 1 = 16, и т.д.
Я попытался разобраться в математике, и в итоге получилось:
b0 = x1
xi = sum(n=0 to i-1)(bn*product(m=n 1 to i-1)(am)
Так, например, для 3-й строки, в которой вы получите:
x3 = a1*a2*b0 b1*a2 b2 = 3*1*3 2*3 1 = 9 6 1 = 16
Но в вычислительном плане это, кажется, хуже, чем просто вычислять каждый x путем перебора строк, что-то вроде этого:
for i in range(2,len(df)):
df.x[i] = df.x[i-1]*df.a[i-1] df.b[i-1]
Есть ли более простой способ решить эту проблему, которого мне не хватает, или я просто имею дело с дорогостоящей вычислительной операцией, которую мне придется выполнять за счет итерации? Если термин a не существует, часть bn может быть обработана с помощью cumsum, что-то вроде:
df['b_cumsum'] = x1 cumsum(df.b)
но в конечном итоге я натыкаюсь на стену при попытке включить термины a, тем более что в конечном итоге нам требуется так много разных наборов продуктов даже в пределах каждого термина sum.
Спасибо.
Комментарии:
1. Я не думаю, что это можно векторизовать, потому что это скользящий расчет. Итак, вы, вероятно, просите нас обойти циклический код
Ответ №1:
Сначала вы можете вычислить масштабированный x: x‘ = x/cumprod(a), используя соответствующий b‘ = b / cumprod(a)
Это может быть сделано с помощью векторизованных операций, а также обратного преобразования из x‘ в x:
ab = np.array([[1, 2],
[3, 1],
[2, 2],
[4, 8],
[1, 9]])
scale = ab.T[0].cumprod()
xp = 3 (ab.T[1]/scale).cumsum()
x = xp*scale
x
array([ 5., 16., 34., 144., 153.])
Комментарии:
1. Это явно работает и выглядит как очень элегантное решение, но не могли бы вы немного объяснить математику? Я понимаю, что масштаб — это просто cumprod (a), но как xp правильно вычисляет x / cumprod (a)? Спасибо.
2. @zachvac Подумайте об этом так: если бы все коэффициенты масштабирования (столбец a ) были равны 1, тогда это было бы легко. Изменяя масштаб x так, как мы это делаем, мы достигаем именно этого. Мы должны использовать cumprod a , потому что исходное масштабирование является кумулятивным. Как только это станет ясно, нам останется только заметить, что b должен масштабироваться так же, как x , для сохранения согласованности.
3. извините, я определенно понял общую цель, возможно, я потратил слишком много времени, пытаясь вывести суммы / продукты, чтобы понять, как это работает. В качестве примера для решения для x5 я получил: [a1-a4] * x1 b1 * [a2-a4] b2 * [a3-a4] b3 * a4 b4, где скобки указывают произведение этих терминов. Как вы можете масштабировать только один раз с помощью cumprod от 1 до 4, когда есть продукты 2-4, 3-4, а затем a4?
4. о, подождите, я разобрался с этим, и я вижу, как это работает, это совокупная сумма совокупных произведений в знаменателе, а числитель отменяет весь знаменатель в 1-м члене, большинство во 2-м и т.д. Я собираюсь изучить это немного подробнее, но это было очень полезно, большое вам спасибо.
5. итак, мое объяснение было таким: мы всегда получаем cumprod (a) * x1 b1 * [a2-an] b2 * [a3-an] … bn итак, x / cumprod (a) = x1 b1 / a1 b2 / a1-a2 … bn / a1-an Поэтому я определенно понимаю, почему это работает, я просто не особенно понимаю объяснение масштабирования или как, возможно, добраться до этого решения без чрезвычайно удачного предположения. Я предполагаю, что понимание того, что [ai-an] / [a1-an] = 1 / (cumprod (a) в i) было бы ключевым, просто кажется, что вы попали туда гораздо более интуитивно понятным способом.
Ответ №2:
Когда я сталкиваюсь с функциями, которые я не могу векторизовать, но это должно быть эффективным, я использую numba
. Который является модулем JIT-компиляции точно в срок. В большинстве случаев это может быть даже быстрее, чем собственные методы pandas:
from numba import njit
@njit
def calculation(arr):
result = np.empty(arr.shape[0])
for idx, row in enumerate(arr):
if idx == 0:
result[idx] = row[2]
else:
row = arr[idx-1]
result[idx] = result[idx-1] * row[0] row[1]
return result
df['x'] = calculation(df.to_numpy())
print(df)
a b x
0 1 2 3.0
1 3 1 5.0
2 2 2 16.0
3 4 8 34.0
4 1 9 144.0
примечание: когда вы хотите рассчитать время. Не рассчитывайте время при первом запуске, поскольку он еще не скомпилирован. Сначала запустите его один раз, затем рассчитайте время для второго запуска.