Вот векторизованный способ получить то, что вы хотите.
Взяв a
из вашего примера:
a[(a < 0.2).any(axis=1).any(axis=1)] = 0.2
print(a)
дает:
array([[[ 0.2 , 0.2 , 0.2 ],
[ 0.2 , 0.2 , 0.2 ],
[ 0.2 , 0.2 , 0.2 ]],
[[ 0.28983508, 0.31122743, 0.67818926],
[ 0.42720309, 0.24416101, 0.5469823 ],
[ 0.22894097, 0.76159389, 0.80416832]],
[[ 0.25661154, 0.64389696, 0.37555374],
[ 0.87871659, 0.27806621, 0.3486518 ],
[ 0.26388296, 0.8993144 , 0.7857116 ]]])
Объяснение:
Возьмем другой пример, где каждый шаг будет более понятным:
a = np.array([[[0.51442898, 0.90447442, 0.45082496],
[0.59301203, 0.30025497, 0.43517362],
[0.28300437, 0.64143037, 0.73974422]],
[[0.228676 , 0.59093859, 0.14441217],
[0.37169639, 0.57230533, 0.81976775],
[0.95988687, 0.43372407, 0.77616701]],
[[0.03098771, 0.80023031, 0.89061113],
[0.86998351, 0.39619143, 0.16036088],
[0.24938437, 0.79131954, 0.38140462]]])
Посмотрим, какие элементы меньше 0,2:
print(a < 0.2)
дает:
array([[[False, False, False],
[False, False, False],
[False, False, False]],
[[False, False, True],
[False, False, False],
[False, False, False]],
[[ True, False, False],
[False, False, True],
[False, False, False]]])
Отсюда мы хотели бы получить индексы тех двумерных массивов, которые имеют хотя бы один элемент True
: [False, True, True]
. Для этого нам требуется np.any
. Обратите внимание, что здесь я буду использовать цепочку методов np.ndarray.any
вместо вызовов вложенных функций np.any
. 1
Теперь просто использование (a < 0.2).any()
даст только True
, потому что по умолчанию он выполняет логическое ИЛИ для всех измерений. Мы должны указать axis
параметр. В нашем случае все будет в порядке с axis=1
или axis=2
. 2
print((a < 0.2).any(axis=1))
дает 3
array([[False, False, False],
[False, False, True],
[ True, False, True]])
Отсюда мы получаем желаемые логические индексы, применяя еще один .any()
вдоль строк:
print((a < 0.2).any(axis=1).any(axis=1))
дает:
array([False, True, True])
В общем, мы можем просто использовать этот массив логических индексов для замены значений исходного массива:
a[(a < 0.2).any(axis=1).any(axis=1)] = 0.2
print(a)
дает:
array([[[0.51442898, 0.90447442, 0.45082496],
[0.59301203, 0.30025497, 0.43517362],
[0.28300437, 0.64143037, 0.73974422]],
[[0.2 , 0.2 , 0.2 ],
[0.2 , 0.2 , 0.2 ],
[0.2 , 0.2 , 0.2 ]],
[[0.2 , 0.2 , 0.2 ],
[0.2 , 0.2 , 0.2 ],
[0.2 , 0.2 , 0.2 ]]])
1 Просто сравните цепочку:
a[(a < 0.2).any(axis=1).any(axis=1)] = 0.2
с вложенностью:
a[np.any(np.any(a < 0.2, axis=1), axis=1)] = 0.2
Я думаю, что последнее более запутанно.
2 Поначалу мне было трудно это понять. Что мне помогло, так это нарисовать изображение куба 3x3x3, распечатать результаты для разных осей и проверить, какая ось соответствует каким направлениям. Кроме того, здесь приведено объяснение использования оси с np.sum
в трехмерном случае: Ось в многомерном массиве Numpy.
3 Можно ожидать, что вы получите [False, True, True]
сразу, но это не так. Для объяснения см. Это: Небольшое уточнение необходимо для numpy.any для матриц