Давайте возьмем пример 3d логической маски из indexing
документов:
In [135]: x = np.arange(30).reshape(2,3,5)
...: b = np.array([[True, True, False], [False, True, True]])
In [136]: x
Out[136]:
array([[[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]],
[[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24],
[25, 26, 27, 28, 29]]])
In [137]: b
Out[137]:
array([[ True, True, False],
[False, True, True]])
In [138]: x[b]
Out[138]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[20, 21, 22, 23, 24],
[25, 26, 27, 28, 29]])
Это 2d массив. Маска b
выделяет элементы из первых двух измерений. Значения False
заставляют пропускать строки [10 ...] и [15 ...].
Мы можем нарезать последнее измерение:
In [139]: x[b,:3]
Out[139]:
array([[ 0, 1, 2],
[ 5, 6, 7],
[20, 21, 22],
[25, 26, 27]])
, но Индекс списка выдаст ошибку (если длина не 4):
In [140]: x[b,[0,1,2]]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-140-7f1dbec100f2> in <module>
----> 1 x[b,[0,1,2]]
IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (4,) (4,) (3,)
Причина в том, что логическая маска эффективно преобразуется в индекс с массивами np.where
:
In [141]: np.nonzero(b)
Out[141]: (array([0, 0, 1, 1]), array([0, 1, 1, 2]))
nonzero
найдено 4 ненулевых элемента. Тогда индексирование x[b]
будет:
In [143]: x[[0,0,1,1],[0,1,1,2],:]
Out[143]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[20, 21, 22, 23, 24],
[25, 26, 27, 28, 29]])
https://docs.scipy.org/doc/numpy/reference/arrays.indexing.html#boolean -индексирование массива
Тогда несоответствие формы становится более очевидным:
In [144]: x[[0,0,1,1],[0,1,1,2],[1,2,3]]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-144-1efd76049cb0> in <module>
----> 1 x[[0,0,1,1],[0,1,1,2],[1,2,3]]
IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (4,) (4,) (3,)
Если списки совпадают по размеру, индексация запускается, но выдает «диагональ», а не блок:
In [145]: x[[0,0,1,1],[0,1,1,2],[1,2,3,4]]
Out[145]: array([ 1, 7, 23, 29])
Как вы обнаружили, двухэтапная индексация работает - но не для значений настройки
In [146]: x[[0,0,1,1],[0,1,1,2]][:,[1,2,3]]
Out[146]:
array([[ 1, 2, 3],
[ 6, 7, 8],
[21, 22, 23],
[26, 27, 28]])
Мы можем получить блок «транспонированием» последнего списка индексов:
In [147]: x[[0,0,1,1],[0,1,1,2],[[1],[2],[3]]]
Out[147]:
array([[ 1, 6, 21, 26],
[ 2, 7, 22, 27],
[ 3, 8, 23, 28]])
Хорошо, это транспонирование. Мы могли бы применить транспонирование к нему. Или мы могли бы сначала транспонировать массивы b
:
In [148]: I,J=np.nonzero(b)
In [149]: x[I[:,None], J[:,None], [1,2,3]]
Out[149]:
array([[ 1, 2, 3],
[ 6, 7, 8],
[21, 22, 23],
[26, 27, 28]])
И это работает для установки
In [150]: x[I[:,None], J[:,None], [1,2,3]]=0
In [151]: x
Out[151]:
array([[[ 0, 0, 0, 0, 4],
[ 5, 0, 0, 0, 9],
[10, 11, 12, 13, 14]],
[[15, 16, 17, 18, 19],
[20, 0, 0, 0, 24],
[25, 0, 0, 0, 29]]])
Это длинный ответ. У меня было общее представление о том, что происходит, но мне нужно было проработать детали. Кроме того, вам нужно понять, что происходит.