Как ускорить цикл в NumPy?
Я хотел бы ускорить этот код:
import numpy as np
import pandas as pd
a = pd.read_csv(path)
closep = a['Clsprc']
delta = np.array(closep.diff())
upgain = np.where(delta >= 0, delta, 0)
downloss = np.where(delta <= 0, -delta, 0)
up = sum(upgain[0:14]) / 14
down = sum(downloss[0:14]) / 14
u = []
d = []
for x in np.nditer(upgain[14:]):
u1 = 13 * up + x
u.append(u1)
up = u1
for y in np.nditer(downloss[14:]):
d1 = 13 * down + y
d.append(d1)
down = d1
Данные ниже:
0 49.00
1 48.76
2 48.52
3 48.28
...
36785758 13.88
36785759 14.65
36785760 13.19
Name: Clsprc, Length: 36785759, dtype: float64
Цикл for слишком медленный, что я могу сделать, чтобы ускорить этот код? Могу ли я векторизовать всю операцию?
2 ответа
Похоже, вы пытаетесь вычислить экспоненциальное скользящее среднее (скользящее среднее), но забыли деление. Если это так, то вы можете увидеть этот вопрос. Между тем, вот быстрая простая скользящая средняя, использующая cumsum()
функция взята по ссылочной ссылке.
def moving_average(a, n=14) :
ret = np.cumsum(a, dtype=float)
ret[n:] = ret[n:] - ret[:-n]
return ret[n - 1:] / n
Если это не так, и вы действительно хотите описанную функцию, вы можете увеличить скорость итерации, используя external_loop
флаг в вашей итерации. Из обалденной документации:
Индитер будет пытаться предоставить куски как можно большего размера для внутреннего цикла. Формируя порядок 'C' и 'F', мы получаем разные размеры внешнего цикла. Этот режим включается указанием флага итератора.
Обратите внимание, что при сохранении собственного порядка памяти по умолчанию итератор может предоставить один одномерный фрагмент, тогда как при форсировании порядка Fortran он должен предоставлять три блока по два элемента каждый.
for x in np.nditer(upgain[14:], flags=['external_loop'], order='F'):
# x now has x[0],x[1], x[2], x[3], x[4], x[5] elements.
Проще говоря, я думаю, что это то, что делают циклы:
upgain=np.array([.1,.2,.3,.4])
u=[]
up=1
for x in upgain:
u1=10*up+x
u.append(u1)
up=u1
производство:
[10.1, 101.2, 1012.3, 10123.4]
np.cumprod([10,10,10,10])
есть плюс модифицированный cumsum
для [.1,.2,.3,.4]
термины. Но я не могу придумать, как объединить их с numpy
функции. Мы могли бы написать обычай ufunc
и использовать его accumulate
, Или мы могли бы написать это в cython
(или другой c
интерфейс).
/questions/23193801/obobschennyie-kumulyativnyie-funktsii-v-numpyscipy/23193809#23193809 предполагает, что frompyfunc
это способ написания обобщенного accumulate
, Я не ожидаю большой экономии времени, может быть, в 2 раза.
Использовать frompyfunc
определить:
def foo(x,y):return 10*x+y
Приложение цикла (выше) будет
def loopfoo(upgain,u,u1):
for x in upgain:
u1=foo(u1,x)
u.append(u1)
return u
"Векторизованная" версия будет:
vfoo=np.frompyfunc(foo,2,1) # 2 in arg, 1 out
vfoo.accumulate(upgain,dtype=object).astype(float)
dtype=object
требование было отмечено в предыдущем SO, и https://github.com/numpy/numpy/issues/4155
In [1195]: loopfoo([1,.1,.2,.3,.4],[],0)
Out[1195]: [1, 10.1, 101.2, 1012.3, 10123.4]
In [1196]: vfoo.accumulate([1,.1,.2,.3,.4],dtype=object)
Out[1196]: array([1.0, 10.1, 101.2, 1012.3, 10123.4], dtype=object)
Для этого небольшого списка, loopfoo
быстрее (3 мкс против 21 мкс)
Для массива из 100 элементов, например biggain=np.linspace(.1,1,100)
, vfoo.accumulate
быстрее:
In [1199]: timeit loopfoo(biggain,[],0)
1000 loops, best of 3: 281 µs per loop
In [1200]: timeit vfoo.accumulate(biggain,dtype=object)
10000 loops, best of 3: 57.4 µs per loop
Для еще большего biggain=np.linspace(.001,.01,1000)
(меньшее число, чтобы избежать переполнения), сохраняется 5-кратное соотношение скоростей.