слишком много индексов для массива при использовании np.where

У меня есть код:

a=b=np.arange(9).reshape(3,3)
c=np.zeros(3)

for x in range(3):
    c[x]=np.average(b[np.where(a<x+3)])

Выход c

>>>array([ 1. , 1.5, 2. ])

Вместо цикла for я хочу использовать массив (векторизацию), тогда я сделал следующий код:

a=b=np.arange(9).reshape(3,3)
c=np.zeros(3)
i=np.arange(3)
c[i]=np.average(b[np.where(a<i[:,None,None]+3)])

Но он показывает IndexError: слишком много индексов для массива

Что касается a<i[:,None,None]+3

это правильно показывает

array([[[ True,  True,  True],
        [False, False, False],
        [False, False, False]],

       [[ True,  True,  True],
        [ True, False, False],
        [False, False, False]],

       [[ True,  True,  True],
        [ True,  True, False],
        [False, False, False]]], dtype=bool)

Но когда я использую b[np.where(a<i[:,None,None]+3)], он снова показывает IndexError: слишком много индексов для массива. Я не могу получить правильный вывод c.


person kinder chen    schedule 13.10.2017    source источник


Ответы (1)


Я чувствую, что вы пытаетесь векторизовать вещи здесь, хотя это явно не упоминается. Я не думаю, что вы можете так индексировать в векторной форме. Чтобы решить ваш вопрос в векторной форме, я бы предложил более эффективный способ получить уменьшение суммы с помощью matrix-multiplication, используя np.tensordot и с помощью broadcasting, как вы уже изложили в своих испытаниях.

Таким образом, одним из решений будет -

from __future__ import division

i = np.arange(3)
mask = a<i[:,None,None]+3
c = np.tensordot(b,mask,axes=((0,1),(1,2)))/mask.sum((1,2))

Связанный пост для понимания tensordot.

Возможные улучшения производительности

  • Преобразуйте маску в dtype с плавающей запятой перед подачей в np.dot, так как с ней матричное умножение на основе BLAS будет быстрее.

  • Используйте np.count_nonzero вместо np.sum для подсчета логических значений. Итак, используйте его для замены mask.sum() детали.

person Divakar    schedule 13.10.2017
comment
Сумма или среднее - это просто пример, на самом деле я просто хочу, чтобы ' b[np.where(a‹i[:,None,None]+3)] работало, я думаю, мне нужно добавить новую ось в b, но Я не знаю как. - person kinder chen; 13.10.2017
comment
@kinderchan Если я правильно понимаю, вы можете использовать: np.broadcast_to(b, mask.shape)[маска]? - person Divakar; 13.10.2017
comment
Пробовал, не работает, тоже использую b[mask,i[:,None]], тоже не получилось. - person kinder chen; 14.10.2017
comment
@kinderchan Вам нужно лучше объяснить свою неработающую часть. Я бы предложил отредактировать ваш вопрос и сообщить нам, что вы ожидаете получить, например: b[np.where(a<i[:,None,None]+3)]. - person Divakar; 14.10.2017
comment
Я повторно отредактировал вопрос, проблема в том, что np.where(a<i[:,None,None]+3) дает 3d-массив, а b - 2d. - person kinder chen; 17.10.2017
comment
@kinderchan Позвольте мне повторить. Пожалуйста, отредактируйте вопрос и сообщите нам выход, который вы ожидаете: b[np.where(a<i[:,None,None]+3)]. Кроме того, посмотрите, даст ли вам это b*mask. - person Divakar; 17.10.2017
comment
Я снова изменил свой вопрос. Что касается вашего способа, я проверил, что np.sum(b*mask,(1,2))/mask.sum((1,2)) быстрее, чем np.tensordot(b,mask,axes=((0,1),(1,2)))/mask.sum((1,2)), и преобразование bool в float также делает алгоритм быстрее. Я действительно ценю твою помощь. - person kinder chen; 18.10.2017
comment
@kinderchan Нет, вы не можете получить ожидаемый результат с np.where. Что касается того, что tensordot медленнее, это невозможно, по крайней мере, с массивами приличного размера, или вы неправильно рассчитали время. - person Divakar; 18.10.2017