У вас абсолютно может быть векторизованное решение с функцией, определяемой пользователем, если эта функция векторизована для поэлементной работы с 1D-массивом (что должно быть в случае всего, написанного с использованием numpy функций вне box).
Допустим, у вас есть r_mat
как матрица (m, n)
и a_array
как (m,)
вектор. Вы можете написать свою функцию для приема хуков. Каждый хук может быть константой или вызываемой. Если это вызываемый объект, он вызывается с двумя массивами одинаковой длины и должен возвращать третий массив такой же длины. Вы можете изменить этот контракт, включив в него индексы или что угодно, по желанию:
def f(r_mat, a_array, hook11, hook01, hook10, hook00):
a = a_array[:, None] # to column vector
row_mask = (r_mat.mean(axis=1) > 2)[:,None]
elem_mask = r_mat >= a
out = np.empty_like(r_mat)
def apply_hook(mask, hook):
r, c = np.nonzero(mask)
out[r, c] = hook(r_mat[r, c], a_array[r]) if callable(hook) else hook
apply_hook(row_mask & elem_mask, hook11)
apply_hook(~row_mask & elem_mask, hook01)
apply_hook(row_mask & ~elem_mask, hook10)
apply_hook(~row_mask & ~elem_mask, hook00)
return out
Текущая конфигурация в вашем коде будет называться как
f(r_mat, a_array, np.subtract, np.add, np.nan, 0)
Допустим, вы хотели что-то сделать сложнее, чем np.subtract
. Например, вы можете сделать:
def my_complicated_func(r, a):
return np.cumsum(r, a) - 3 * r // a + np.exp(a)
f(r_mat, a_array, my_complicated_func, np.add, np.nan, 0.0)
Ключ в том, что my_complicated_func
работает с массивами. Ему будет передано подмножество элементов r_mat
и элементов a_array
, дублированных столько раз, сколько необходимо в каждой строке.
Вы также можете сделать то же самое с функцией, зная о индекс каждого места. Просто позвоните hook
как hook(r_mat[r, c], a_array[r], r, c)
. Теперь функции ловушки должны принимать два дополнительных аргумента. Исходный код будет эквивалентен
f(r_mat, a_array, lambda r, a, *args: np.subtract(r, a), lambda r, a, *args: np.add(r, a), np.nan, 0)