Есть ли способ применить функцию numpy, которая принимает два массива 1d в качестве аргументов в каждой строке двух 2d массивов вместе?

#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, чтобы убедиться, что выходные данные имеют одинаковый размер. Я обновлю свой пример, чтобы отразить это.