Цикл векторизации с рабочим значением, которое зависит от предыдущего значения (+ оператор if) - PullRequest
0 голосов
/ 27 ноября 2018

Я привык писать векторизованные операторы и списочные выражения в Python, но у меня есть проблема, которая возникает как при «работающих» вычислениях, которые зависят от предыдущего значения в цикле, так и при выполнении оператора if.Схематически это выглядит так:

def my_loop(x, a=0.5, b=0.9):
  out = np.copy(x)
  prev_val = 0 
  for i in np.arange(x.shape[0]):

      if x[i] < prev_val:
          new_val = (1-a)*x[i] + a*prev_val
      else:
          new_val = (1-b)*x[i] + b*prev_val

      out[i] = new_val

      prev_val = new_val

  return out

Я не смог выяснить, как можно векторизовать это (например, с помощью какого-то аккумулятора), поэтому я спрошу: есть ли способсделать это более Pythonic / быстрее?

Я видел предыдущие посты о векторизации, когда есть оператор if - обычно решаемый с помощью np.where () - но не тот, где есть «бегущее» значение, которое зависит от его предыдущего состояния ...поэтому я еще не нашел повторяющихся вопросов (и этот не о векторизации в обычном смысле, этот о «предыдущем значении», но ссылается на индексы списка),

До сих пор я пробовал np.vectorize и numba @jit, и они действительно работают несколько быстрее, но ни одна из них не дает мне скорости, на которую я надеюсь.Я что-то упускаю?(Может что-то с map()?) Спасибо.

(Да, в случае a = b это становится легко!)

Ответы [ 2 ]

0 голосов
/ 27 ноября 2018

JITing в режиме nopython быстрее.Цитирование из документов Numba:

Numba имеет два режима компиляции: режим nopython и режим объекта.Первый производит намного более быстрый код, но имеет ограничения, которые могут заставить Numba переключиться на последний.Чтобы предотвратить отступление Numba и вместо этого вызвать ошибку, передайте nopython = True.

@nb.njit(cache=True)
def my_loop5(x, a=0.5, b=0.9):
  out = np.zeros(x.shape[0],dtype=x.dtype)

  for i in range(x.shape[0]):
      if x[i] < out[i-1]:
          out[i] = (1-a) * x[i] + a * out[i-1]
      else:
          out[i] = (1-b) * x[i] + b * out[i-1]
  return out

Чтобы включить:

x = np.random.uniform(low=-5.0, high=5.0, size=(1000000,))

Время:

my_loop4: 0,235 с

my_loop5: 0,193 с

HTH.

0 голосов
/ 27 ноября 2018

Я понял, что, удалив фиктивные переменные, этот код можно было бы преобразовать в форму, в которой numba и @autojit могли бы творить свою магию и делать ее «быстрой»:

from numba import jit, autojit

@autojit
def my_loop4(x, a=0.5, b=0.9):
  out = np.zeros(x.shape[0],dtype=x.dtype)
  for i in np.arange(x.shape[0]):
      if x[i] < out[i-1]:
          out[i] = (1-a)*x[i] + a*out[i-1]
      else:
          out[i] = (1-b)*x[i] + b*out[i-1]
  return out

Без @autojit,это все еще мучительно медленно.Но с этим ... проблема решена.Таким образом, удаление ненужных переменных и с добавлением @autojit - вот что сработало.

...