Замените все элементы массива 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)]