2016-12-21 16 views
0

Я довольно новичок в numpy. У меня есть следующий код, который дает мне желаемый результат:Как векторизовать цикл for, содержащий if-statement в numpy?

import numpy as np 

def get_result(S,K,delS): 
    res=np.zeros(S.shape[0],dtype=np.float64) 
    for j in range(res.shape[0]): 
     if S[j]-K>delS: 
      res[j]+=np.floor((S[j]-K)/delS) 
      K+=np.floor((S[j]-K)/delS)*delS 
     elif S[j]-K<-delS: 
      res[j]+=np.ceil((S[j]-K)/delS) 
      K+=np.ceil((S[j]-K)/delS)*delS 
    return res 

S=np.array([1.0,1.05,1.1,1.12,1.09,1.14,1.21,1.6,1.05,1.0,0.95,0.90,0.87,0.77,0.63,0.85,0.91,0.76],dtype=np.float64) 

K=1.0 
delS=0.1 

l=get_result(S,K,delS) 

for j in range(S.shape[0]): 
    print("%d\t%.2f\t%.0f" % (j,S[j],l[j])) 

Функция get_result содержит для цикла, однако, и поэтому неловко медленно для больших входных векторов S. Может ли такая функция векторизовать в NumPy -синтаксис? Любая помощь будет оценена по достоинству.

+0

Я был первым под впечатлением, что вся функция может быть векторизации всего три строки кода, но потом заметил, что 'k' меняется в цикле. Поскольку для вычисления K [j + 1] требуется знание K [j], существуют зависимости между итерациями, и цикл не может быть векторизован. По крайней мере, насколько мне известно. – DyZ

+0

См. Это [ответ] (http://stackoverflow.com/a/41244906/2593236) по аналогичной проблеме. – pbreach

+0

Если вы используете 'Python 2.x', замените' range' (список возврата) на 'xrange' (возвращает генератор) для значительного ускорения. – DerWeh

ответ

1

Общая картина при работе с 2 или более условиями в массиве или вычислениях, чтобы построить булеву маску, и принять одно действия для элементов, где, что маска является истинной, и другое, где оно ложно:

res=np.zeros(S.shape[0],dtype=np.float64) 
mask - S-K>delS 
res[mask] = ...S[mask] 
res[~mask] = ...S[~mask] 

Вариант состоит в том, чтобы идентифицировать индексы, с np.where(mask).

Но то, что, похоже, усложняет ваш расчет, заключается в том, что тест продолжает меняться. То есть K для j+1 происходит от расчета по j.

for j in range(res.shape[0]): 
    if S[j]-K>delS: 
     res[j]+=np.floor((S[j]-K)/delS) 
     K+=np.floor((S[j]-K)/delS)*delS 
    elif S[j]-K<-delS: 
     res[j]+=np.ceil((S[j]-K)/delS) 
     K+=np.ceil((S[j]-K)/delS)*delS 

Чтобы справиться с такой итерации мы обычно пытаются использовать np.cumsum или другие накопительные ufunc методы.

Как правило, код numpy является самым быстрым и легким, когда вычисление может быть применено ко всем элементам массива (или набору массивов) «параллельно», то есть таким образом, чтобы это не зависело от порядок итерации. Затем мы можем делегировать действие быстрым скомпилированным функциям numpy, таким как сложение и умножение массива. Если вычисление носит серийный характер (значение j в зависимости от j-1), это становится более сложным.

Так что, если мое беглое чтение правильное, это не сложность if-утверждения, это последовательный характер цикла.

=========================

Игра вокруг, я был в состоянии удалить if (есть на самом деле 3 подслучай) , но он все еще должен быть итеративным:

def get_result(S,K,delS): 
    S = S/delS 
    res=np.zeros(S.shape[0],dtype=np.float64) 
    for j in range(res.shape[0]): 
     x = S[j] - K/delS 
     xa = np.floor(np.abs(x)) * np.sign(x) 
     res[j] += xa 
     K += xa*delS   
    return res 
+0

Да, все ваши комментарии верны и полезны. – user5507059

+0

Суть в том, что этот тип функции не может быть легко векторизован, особенно если зависимость от предыдущих значений в цикле становится еще более сложной. Для справки: я собираюсь cythonize мой for-loop. Это делает его быстрым и сохраняет читаемый код. – user5507059

+1

Да, это похоже на хороший кандидат на cython, особенно если вы можете избежать числовых версий 'floor',' ceiling', 'abs' и т. Д. – hpaulj

 Смежные вопросы

  • Нет связанных вопросов^_^