Замініть всі елементи Python NumPy масиву, які перевищують якесь значення


190

У мене є 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
  1. Який найбільш стислий і пітонічний спосіб це зробити?

  2. Чи існує швидший (можливо, менш стислий та / або менш пітонічний) спосіб зробити це?

Це буде частиною підпрограми регулювання рівня вікна / рівня для МРТ людської голови. 2D масивний масив - це дані пікселів зображення.


Для отримання додаткової інформації погляньте на це вступ до індексації .
askewchan

Відповіді:


332

Я думаю, що як найшвидший і стисліший спосіб це зробити - це використовувати вбудовану індексацію Fancy 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

3
Зауважте, що це змінює існуючий масив arr, а не створює resultмасив, як в ОП.
askewchan

1
Чи є спосіб це зробити, не змінюючи, Aа створюючи новий масив?
натрійнітрат

Що б ми зробили, якби хотіли змінити значення в індексах, кратних заданому n, як [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, здається, не є тим, що мені потрібно в цьому випадку, в ОП ви бачите, що поріг T і значення заміни (255) не обов'язково однакові число. Однак я все-таки дав вам голос за ретельність. Знову дякую.
NLi10Me

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

@lavee_singh, щоб зробити це, ви можете використовувати третю частину зрізу, яка, як правило , нехтували: a[start:stop:step]дає елементи масиву від startдо 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)

Це рішення, яке я використав, тому що це перше, на що я потрапив. Цікаво, чи є велика різниця між цим та обраною відповіддю вище. Що ти думаєш?
Jonathanking

У моїх дуже обмежених тестах мій вище код з np.place працює на 2 рази повільніше, ніж метод прямого індексування прийнятого відповіді. Це дивно, тому що я б подумав, що np.place буде більш оптимізованим, але, мабуть, вони, напевно, поклали більше роботи на пряму індексацію.
Shital Shah

У моєму випадку 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.