NumPy векторизация вдоль оси всегда возможна?

#python #numpy #vectorization

#python #numpy #векторизация

Вопрос:

Мне нужно выполнить вычисления вдоль оси массива NumPy, и я хотел бы знать, есть ли какой-либо способ обойти цикл for, поскольку это кажется очень медленным. Обычно я всегда пытаюсь векторизовать свой код, но с этим вычислением я просто не знаю как.

Итак, допустим, у меня есть 2d-массив X с формой (7000 x 10). Примерно половина записей — это значения NaN. Теперь я просматриваю каждую строку X (с именем x_i), которая представляет собой одномерный массив с формой (10). Основываясь на индексах значений, отличных от NaN в x_i, я разделяю 1d-массив M, форма (10), и 2d-массив C, форма (10 x 10). И затем я выполняю некоторые вычисления с ним и повторяю все вдоль первой оси X.

Мой код (довольно упрощенный) выглядит более или менее так:

 X = np.random.random_sample((7000, 10))  # Note that in the real case, X also has a lot of NaN values
M = np.random.random_sample(10)
C = np.random.random_sample((10, 10))

Res = np.empty(X.shape[0])   # Preallocation of result

for i in range(X.shape[0]):
    # Get row i of X
    x_i = X[i]

    # Get indices of the non-NaN values in x_i
    index_not_nan_i = np.where(~np.isnan(x_i))[0]
    
    # Partition M and C according to indices
    M_i = M[index_not_nan_i]
    C_i = C[index_not_nan_i[..., None], index_not_nan_i]


    Res[i] = M_i @ C_i @ M_i
  

Пример:

Допустим, первая строка X выглядит следующим образом x_i = [5, 3, 6, NaN, 7, NaN, NaN, 0, 5, NaN]; Таким образом, index_not_nan_i = [0,1,2,4,7,8]. Поскольку в x_i есть 6 значений, отличных от NaN, M_i будет иметь форму (6), а C_i будет иметь форму (6,6). Следующий x_i, конечно, будет иметь NaN-значения в других позициях, поэтому index_not_nan_i, возможно, будет [3,7,5], а M_i и C_i будут иметь форму (3) и (3 x 3).

Существуют ли какие-либо возможности векторизации? Я уже пробовал numpy.apply_along_axis(), но это, похоже, обычный цикл for с точки зрения производительности. Также, согласно документации, numpy.vectorize() также кажется, что это цикл for, хотя я еще не пробовал.

Комментарии:

1. могут ли M и C не быть размером и формой X? Тогда просто примените 2D-маску?

2. numpy у него нет инструмента для повышения производительности простым переносом.

3. @GhandiFloss На самом деле есть еще одно измерение, связанное с размером 19000. Итак, C на самом деле имеет форму 19000x10x10, и если бы я транслировал его в соответствии с формой X, у меня был бы массив формы 7000x19000x10x10. И тогда у меня просто заканчивается память.

4. Обычная векторизация использует широковещательную передачу и axis параметры для использования быстро скомпилированного кода в массивах более высокой размерности. @ например, можно выполнить dot продукт на batches (первые измерения). Но это может быть бесполезно, если ваши M_i и C_i отличаются по размеру в зависимости от i . В общем, «векторизация» затруднена при работе с массивами, которые отличаются по размеру (где вы не можете создавать 3d массивы).

5. Можете ли вы преобразовать X в 0 и 1 для NaN, а не для NaN. Затем получите ваши массивы путем умножения.