Я бы сделал что-то вроде этого:
def updown(x):
result = np.zeros_like(x)
for i in range(len(x)):
mid = (len(x)+1)//2
a = x[:mid].mean(axis=0)
b = x[-mid:].mean(axis=0)
mask_a = a != 0
mask_b = (a == 0) & (b != 0)
result[i, mask_a] = (b[mask_a] - a[mask_a]) / abs(a[mask_a])
result[i, mask_b] = (b[mask_b] - a[mask_b]) / abs((a[mask_b] + b[mask_b])/2)
x = x[1:]
return result
В тех случаях, когда al oop довольно неизбежно, как здесь, лучше предварительно выделить массив результатов (если размеры известны заранее) и заполните значения в l oop. Это также имеет то преимущество, что позволяет предварительно заполнить значение по умолчанию, равное нулю.
Нет необходимости применять что-либо к столбцам x
в качестве других операций (mean
и arithmeti * 1032). *) может быть легко применен к выбранной оси.
Условный лог c достигается с помощью маскировки.
Вы также можете использовать аргументы ключевых слов out
и where
из np.divide
:
def updown(x):
result = np.zeros_like(x)
for i in range(len(x)):
mid = (len(x)+1)//2
a = x[:mid].mean(axis=0)
b = x[-mid:].mean(axis=0)
np.divide(b - a, abs(a), out=result[i], where=(a != 0))
np.divide(b - a, abs((a + b)/2), out=result[i], where=(a == 0) & (b != 0))
x = x[1:]
return result
Давайте посмотрим некоторые сравнения производительности (я завернул весь ваш исходный код в функцию с именем updown_orig
)
Для измерений (20, 5)
мы видим улучшение только в 2 раза:
In []: %timeit updown_orig(np.random.rand(20, 5))
1 ms ± 13.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In []: %timeit updown(np.random.rand(20, 5))
508 µs ± 10.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Давайте масштабируем оба измерения на 10 (увеличив размер x
в 100 раз):
In []: %timeit updown_orig(np.random.rand(200, 50))
96.4 ms ± 1.91 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In []: %timeit updown(np.random.rand(200, 50))
5.84 ms ± 64 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Теперь разница коэффициент около 16,5.