Эффективные математические операции над частями "редких" массивов - PullRequest
1 голос
/ 11 марта 2011

У меня есть следующая задача в симуляции для моей кандидатской диссертации:

Мне нужно оптимизировать следующий код:

repelling_forces = repelling_force_prefactor * np.exp(-(height_r_t/potential_steepness))

В этом коде фрагмент 'height_r_t' является настоящим массивом Numpy, а 'factor_steepness' является скаляром. 'repelling_force_prefactor' также является массивом Numpy, который в основном равен нулю, но ОДИН в предварительно рассчитанной позиции, которые НЕ меняются во время выполнения (то есть маски). Очевидно, что код неэффективен, так как было бы гораздо разумнее вычислять экспоненциальную функцию только в тех позициях, где 'repelling_force_prefactor' не равен нулю.

Вопрос в том, как мне сделать это наиболее эффективным способом?

Единственная идея, которая у меня есть до сих пор, состоит в том, чтобы определить срез для 'height_r_t', используя 'repelling_force_prefactor', и применить 'np.exp' к этим срезам. Однако я понял, что нарезка медленная (не уверен, что это в целом правильно), и решение кажется неуклюжим.

Точно так же, как примечание, отношение «1» к «0» в «repelling_force_prefactor» составляет около 1/1000, и я выполняю это в цикле, поэтому эффективность очень важна. (Комментарий: у меня не было бы проблем с обращением к Cython, так как мне все равно понадобится / хочу изучить его в какой-то момент ... но я новичок, поэтому мне нужен хороший указатель / объяснение.)

Ответы [ 2 ]

3 голосов
/ 12 марта 2011

замаскированные массивы реализованы именно для ваших целей.

Производительность такая же, как и у ответа Свена:

height_r_t = np.ma.masked_where(repelling_force_prefactor == 0, height_r_t)
repelling_forces = np.ma.exp(-(height_r_t/potential_steepness))

преимущество замаскированных массивов состоит в том, что вам не нужно нарезать и расширятьваш массив, размер всегда один и тот же, но numpy автоматически знает, что не нужно вычислять exp, где массив замаскирован.

Кроме того, вы можете суммировать массив с разными масками, и результирующий массив имеет пересечение масок.

2 голосов
/ 11 марта 2011

Нарезка, вероятно, намного быстрее, чем вычисление всех экспонент. Вместо того, чтобы использовать маску repelling_force_prefactor для прямого среза, я предлагаю предварительно рассчитать индексы, где она не равна нулю, и использовать их для нарезки:

# before the loop
indices = np.nonzero(repelling_force_prefactor)

# inside the loop
repelling_forces = np.exp(-(height_r_t[indices]/potential_steepness))

Теперь repelling_forces будет содержать только ненулевые результаты. Если вам нужно обновить какой-либо массив исходной формы height_r_t с этими значениями, вы можете снова использовать нарезку с indices или использовать np.put() или аналогичную функцию.

Нарезка со списком индексов в этом случае будет более эффективной, чем нарезка с помощью логической маски, поскольку список индексов короче в тысячу раз. Фактическое измерение производительности, конечно, зависит от вас.

...