Замените все элементы массива Python NumPy, которые больше некоторого значения
У меня есть массив 2D NumPy и я хотел бы заменить все значения в нем, превышающие или равные порогу T с 255.0. Насколько я знаю, самым фундаментальным способом было бы:
shape = arr.shape
result = np.zeros(shape)
for x in range(0, shape[0]):
for y in range(0, shape[1]):
if arr[x, y] >= T:
result[x, y] = 255
-
Каков самый лаконичный и питонический способ сделать это?
-
Есть ли более быстрый (возможно, менее краткий и/или менее питонический) способ сделать это?
Это будет частью подпрограммы настройки окна/уровня для МРТ-сканирования человеческой головы. Массив 2D numpy - это данные пикселя изображения.
Ответы
Ответ 1
Я думаю, что самый быстрый и краткий способ сделать это - использовать встроенную функцию индексирования Fancy в NumPy. Если у вас есть ndarray
именем arr
, вы можете заменить все элементы >255
значением x
следующим образом:
arr[arr > 255] = x
Я запустил это на своей машине со случайной матрицей 500 x 500, заменив все значения> 0,5 на 5, и это заняло в среднем 7,59 мс.
In [1]: import numpy as np
In [2]: A = np.random.rand(500, 500)
In [3]: timeit A[A > 0.5] = 5
100 loops, best of 3: 7.59 ms per loop
Ответ 2
Поскольку вам действительно нужен другой массив, который arr
где arr < 255
и 255
в противном случае, это можно сделать просто:
result = np.minimum(arr, 255)
В более общем плане для нижней и/или верхней границы:
result = np.clip(arr, 0, 255)
Если вы просто хотите получить доступ к значениям более 255 или что-то более сложное, ответ @mtitan8 будет более общим, но np.clip
и np.minimum
(или np.maximum
) будут лучше и быстрее для вашего случая:
In [292]: timeit np.minimum(a, 255)
100000 loops, best of 3: 19.6 µs per loop
In [293]: %%timeit
.....: c = np.copy(a)
.....: c[a>255] = 255
.....:
10000 loops, best of 3: 86.6 µs per loop
Если вы хотите сделать это на месте (т.е. измените arr
вместо создания result
), вы можете использовать параметр out
np.minimum
:
np.minimum(arr, 255, out=arr)
или
np.clip(arr, 0, 255, arr)
(имя out=
является необязательным, поскольку аргументы в том же порядке, что и определение функции.)
Для модификации на месте булевская индексация ускоряет много (без необходимости делать, а затем модифицировать копию отдельно), но все еще не так быстро, как minimum
:
In [328]: %%timeit
.....: a = np.random.randint(0, 300, (100,100))
.....: np.minimum(a, 255, a)
.....:
100000 loops, best of 3: 303 µs per loop
In [329]: %%timeit
.....: a = np.random.randint(0, 300, (100,100))
.....: a[a>255] = 255
.....:
100000 loops, best of 3: 356 µs per loop
Для сравнения, если вы хотите ограничить свои значения как минимум, так и максимум, без clip
, вам нужно будет сделать это дважды, с чем-то вроде
np.minimum(a, 255, a)
np.maximum(a, 0, a)
или,
a[a>255] = 255
a[a<0] = 0
Ответ 3
Вы можете использовать numpy.putmask:
np.putmask(arr, arr>=T, 255.0)
Ниже приведено сравнение производительности с встроенной индексацией Numpy:
In [1]: import numpy as np
In [2]: A = np.random.rand(500, 500)
In [3]: timeit np.putmask(A, A>0.5, 5)
1000 loops, best of 3: 1.34 ms per loop
In [4]: timeit A[A > 0.5] = 5
1000 loops, best of 3: 1.82 ms per loop
Ответ 4
Я думаю, что вы можете достичь этого быстрее, используя функцию where
:
Например, поиск элементов больше 0,2 в массиве numpy и их замена 0:
import numpy as np
nums = np.random.rand(4,3)
print np.where(nums > 0.2, 0, nums)
Ответ 5
Другой способ - использовать np.place
который выполняет замену на месте и работает с многомерными массивами:
import numpy as np
arr = np.arange(6).reshape(2, 3)
np.place(arr, arr == 0, -10)
Ответ 6
Вы также можете использовать &
, |
(и/или) для большей гибкости:
значения между 5 и 10: A[(A>5)&(A<10)]
значения больше 10 или меньше 5: A[(A<5)|(A>10)]