Substitua todos os elementos do Python NumPy Array que são maiores que algum valor

190

Eu tenho uma matriz NumPy 2D e gostaria de substituir todos os valores nela maiores ou iguais a um limite T com 255.0. Que eu saiba, a maneira mais fundamental seria:

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. Qual é a maneira mais concisa e pitônica de fazer isso?

  2. Existe uma maneira mais rápida (possivelmente menos concisa e / ou menos pitônica) de fazer isso?

Isso fará parte de uma sub-rotina de ajuste de janela / nível para exames de ressonância magnética da cabeça humana. A matriz numpy 2D é o dado de pixel da imagem.

NLi10Me
fonte
Para obter mais informações, consulte esta introdução à indexação .
askewchan

Respostas:

333

Acho que a maneira mais rápida e concisa de fazer isso é usar a indexação Fancy incorporada do NumPy. Se você tiver um ndarraynome arr, poderá substituir todos os elementos >255por um valor da xseguinte maneira:

arr[arr > 255] = x

Eu executei isso na minha máquina com uma matriz aleatória 500 x 500, substituindo todos os valores> 0,5 por 5, e levou uma média de 7,59ms.

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
mdml
fonte
3
Observe que isso modifica a matriz existente arr, em vez de criar uma resultmatriz como no OP.
askewchan
1
Existe uma maneira de fazer isso não modificando, Amas criando uma nova matriz?
nitrato de sódio 25/08
O que faríamos se quiséssemos alterar valores em índices múltiplos de n, como a [2], a [4], a [6], a [8] ..... para n = 2?
Lavee_singh
100 loops, o melhor de 3: 2,22 ms por loop
dreab
5
NOTA: Isto não funciona se os dados estiverem em uma lista de python, ele tem que estar em uma matriz numpy ( np.array([1,2,3])
mjp
46

Como você realmente deseja uma matriz diferente, que é arronde arr < 255e 255, caso contrário, isso pode ser feito simplesmente:

result = np.minimum(arr, 255)

De maneira mais geral, para um limite inferior e / ou superior:

result = np.clip(arr, 0, 255)

Se você deseja apenas acessar valores acima de 255, ou algo mais complicado, a resposta do @ mtitan8 é mais geral, mas np.clipe np.minimum(ou np.maximum) é mais agradável e muito mais rápida para o seu caso:

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

Se você quiser fazer isso no local (por exemplo, modificar em arrvez de criar result), poderá usar o outparâmetro de np.minimum:

np.minimum(arr, 255, out=arr)

ou

np.clip(arr, 0, 255, arr)

(o out=nome é opcional, pois os argumentos estão na mesma ordem que a definição da função.)

Para a modificação no local, a indexação booleana acelera bastante (sem precisar fazer e modificar a cópia separadamente), mas ainda não é tão rápida quanto 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

Para comparação, se você quiser restringir seus valores com um mínimo e um máximo, sem clipisso, você precisará fazer isso duas vezes, com algo como

np.minimum(a, 255, a)
np.maximum(a, 0, a)

ou,

a[a>255] = 255
a[a<0] = 0
askewchan
fonte
1
Muito obrigado pelo seu comentário completo, no entanto, np.clip e np.minimum não parecem ser o que eu preciso neste caso, no OP você vê que o limite T e o valor de substituição (255) não são necessariamente os mesmos número. No entanto, ainda lhe dei um voto de profundidade. Obrigado novamente.
NLi10Me 30/10/2013
O que faríamos se quiséssemos alterar valores em índices múltiplos de n, como a [2], a [4], a [6], a [8] ..... para n = 2?
Lavee_singh
@lavee_singh, para fazer isso, você pode usar a terceira parte da fatia, que geralmente é negligenciada: a[start:stop:step]fornece os elementos da matriz de startpara stop, mas, em vez de todos os elementos, são necessários apenas todos step(se negligenciados, é 1por padrão). ) Então, para definir todos os nivela a zero, você poderia fazera[::2] = 0
askewchan
Obrigado, eu precisava de algo assim, embora soubesse para listas simples, mas não sabia se ou como funciona para numpy.array.
Lavee_singh
14

Eu acho que você pode conseguir isso o mais rápido usando o where função:

Por exemplo, procurando itens maiores que 0,2 em uma matriz numpy e substituindo aqueles por 0:

import numpy as np

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

print np.where(nums > 0.2, 0, nums)
Amir F
fonte
10

Você pode considerar o uso de numpy.putmask :

np.putmask(arr, arr>=T, 255.0)

Aqui está uma comparação de desempenho com a indexação interna do 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
lev
fonte
8

Outra maneira é usar o np.placeque substitui no local e trabalha com matrizes multidimensionais:

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)
Shital Shah
fonte
Esta é a solução que usei porque foi a primeira que me deparei. Gostaria de saber se existe uma grande diferença entre essa e a resposta selecionada acima. O que você acha?
18718 jonathanking
Em meus testes muito limitados, meu código acima com np.place está executando 2X mais lentamente que o método de indexação direta da resposta aceita. É surpreendente, porque eu pensaria que o np.place seria mais otimizado, mas acho que eles provavelmente colocaram mais trabalho na indexação direta.
Shital Shah
No meu caso, np.placetambém foi mais lento em comparação com o método interno, embora o contrário seja reivindicado neste comentário.
riyansh.legend
3

Você também pode usar &,| (e / ou) para mais flexibilidade:

valores entre 5 e 10: A[(A>5)&(A<10)]

valores maiores que 10 ou menores que 5: A[(A<5)|(A>10)]

Mahdi Shahbaba
fonte