#python #arrays #numpy
Вопрос:
Я пытаюсь запустить что-то вроде:
np.bincount(array1, weights = array2, minlength=7)
где оба array1
и array2
являются 2d n массивами numpy формы (m,n). Моя желаемая цель состоит в том, чтобы np.bincount()
выполнить n раз с каждой строкой array1 и array2
Я пытался использовать np.apply_along_axis (), но, насколько я могу судить, это позволяет запускать функцию только в каждой строке array1 без использования каждой строки array2 в качестве аргументов np.bincount
. Я надеялся найти способ сделать это чисто с помощью функции numpy, а не итерации, поскольку это критичная для производительности функция, но пока не могу найти другого способа.
Например, учитывая эти массивы:
array1 = [[1,2,3],[4,5,6]]
array2 = [[7,8,9],[10,11,12]]
Я бы хотел вычислить:
[np.bincounts([1,2,3], weights = [7,8,9],minlength=7), np.bincounts([4,5,6], weights = [10,11,12], minlength=7)]
Комментарии:
1. Всегда полезно привести небольшой пример входных данных и ожидаемых результатов…
2.
apply_along...
не является инструментом производительности даже там, где он работает. Он не компилирует функцию и не выполняет циклыc
.3. Общего способа сделать это не существует. Если функцию нужно вызывать один раз для каждой пары строк, это займет время, независимо от метода итерации или оболочки.Иногда можно переосмыслить проблему и решить ее для всех строк сразу. Но
bincount
получает свою скорость, работая в 1d.
Ответ №1:
Простое решение-просто использовать списки понимания:
result = [np.bincount(v, weights=w) for v,w in zip(array1, array2)]
Поскольку результирующие массивы могут иметь разный размер (и на самом деле имеют разный размер в вашем примере), результатом может быть не массив Numpy, а обычный список. Большинство функций Numpy не могут работать со списком массивов переменного размера или даже создавать их.
Если у вас много строк в массивах, вы можете снизить стоимость циклов интерпретатора CPython, используя JIT Numba (или, в конечном счете, Cython в данном случае). Обратите внимание, что входные массивы должны быть преобразованы в массивы Numpy перед вызовом функции Numba для повышения производительности. Если вы знаете, что все массивы имеют одинаковый размер, вы можете написать более эффективную реализацию с помощью Numba (предварительно распределив полученный массив и выполнив подсчет самостоятельно).
Обновить
С массивами фиксированного размера вот быстрая реализация в Numba:
import numpy as np
import numba as nb
array1 = np.array([[1,2,3],[4,5,6]], dtype=np.int32)
array2 = np.array([[7,8,9],[10,11,12]], dtype=np.int32)
@nb.njit('i4[:,::1](i4[:,::1],i4[:,::1])')
def compute(array1, array2):
assert array1.shape == array2.shape
n, m = array1.shape
res = np.zeros((n, 7), dtype=np.int32)
for i in range(n):
for j in range(m):
v = array1[i, j]
assert v>=0 and v<7 # Can be removed if the input is safe
res[i, v] = array2[i, j]
return res
result = compute(array1, array2)
# result is
# array([[ 0, 7, 8, 9, 0, 0, 0],
# [ 0, 0, 0, 0, 10, 11, 12]])
Комментарии:
1. Это мое текущее решение. С тех пор как я задал этот вопрос, я понял, что мои выходные данные были разных размеров, и теперь использую аргумент minlength, чтобы убедиться, что выходные данные имеют одинаковый размер. Я обновлю свой пример, чтобы отразить это.