Заменить все элементы Python NumPy Array, которые больше некоторого значения


191

У меня есть двумерный массив 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
  1. Какой самый лаконичный и питонный способ сделать это?

  2. Есть ли более быстрый (возможно, менее лаконичный и / или менее питонический) способ сделать это?

Это будет частью процедуры настройки окна / уровня для МРТ-сканирования головы человека. Двумерный массив - это данные пикселей изображения.


Для получения дополнительной информации взгляните на это введение в индексирование .
askewchan

Ответы:


334

Я думаю, что самый быстрый и краткий способ сделать это - использовать встроенную в NumPy индексацию Fancy. Если у вас есть 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

3
Обратите внимание, что это изменяет существующий массив arr, а не создает resultмассив, как в OP.
Askewchan

1
Есть ли способ сделать это, не изменяя, Aно создавая новый массив?
нитрат натрия

Что бы мы делали, если бы мы хотели изменить значения по индексам, кратным заданному n, например a [2], a [4], a [6], a [8] ..... для n = 2?
lavee_singh

100 циклов, лучшее из 3: 2,22 мс на цикл
страшно,

5
ПРИМЕЧАНИЕ: это не работает, если данные находятся в списке python, они ДОЛЖНЫ быть в массиве numpy ( np.array([1,2,3])
mjp

46

Так как вы на самом деле хотите другой массив, который находится 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

1
Большое спасибо за ваш полный комментарий, однако np.clip и np.minimum не кажутся мне необходимыми в этом случае, в OP вы видите, что порог T и значение замены (255) не обязательно совпадают число. Тем не менее, я все же дал вам голос за тщательность. Еще раз спасибо.
NLi10Me

Что бы мы делали, если бы мы хотели изменить значения по индексам, кратным заданному n, например a [2], a [4], a [6], a [8] ..... для n = 2?
lavee_singh

@lavee_singh, чтобы сделать это, вы можете использовать третью часть среза, которой обычно пренебрегают: a[start:stop:step]выдает элементы массива от startto stop, но вместо каждого элемента он принимает только каждый step(если пренебречь, 1по умолчанию ). Таким образом, чтобы установить все вечера на ноль, вы можете сделатьa[::2] = 0
askewchan

Спасибо, мне нужно что-то вроде этого, хотя я знал это для простых списков, но я не знал, работает ли он или как это работает для numpy.array.
lavee_singh

14

Я думаю, что вы можете достичь этого быстрее, используя whereфункцию:

Например, ищем элементы больше 0,2 в массиве numpy и заменяем их на 0:

import numpy as np

nums = np.random.rand(4,3)

print np.where(nums > 0.2, 0, nums)

10

Вы можете рассмотреть возможность использования 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

8

Другой способ - использовать np.placeзамену на месте и работать с многомерными массивами:

import numpy as np

# create 2x3 array with numbers 0..5
arr = np.arange(6).reshape(2, 3)

# replace 0 with -10
np.place(arr, arr == 0, -10)

Это решение, которое я использовал, потому что оно было первым, с которым я столкнулся. Интересно, есть ли большая разница между этим и выбранным ответом выше. Что вы думаете?
Джонатанкинг

В моих очень ограниченных тестах мой приведенный выше код с np.place работает в 2 раза медленнее, чем принятый ответ метод прямой индексации. Это удивительно, потому что я думал, что np.place будет более оптимизированным, но я думаю, что они, вероятно, проделали большую работу по прямой индексации.
Шиталь Шах,

В моем случае np.placeтакже был медленнее по сравнению со встроенным методом, хотя в этом комментарии утверждается обратное .
riyansh.legend

3

Вы можете также использовать &, |(и / или) для большей гибкости:

значения между 5 и 10: A[(A>5)&(A<10)]

значения больше 10 или меньше 5: A[(A<5)|(A>10)]

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.