Como seleciono elementos de uma matriz com uma condição?

156

Suponha que eu tenho uma matriz numpy x = [5, 2, 3, 1, 4, 5], y = ['f', 'o', 'o', 'b', 'a', 'r']. Desejo selecionar os elementos ycorrespondentes aos elementos xmaiores que 1 e menores que 5.

eu tentei

x = array([5, 2, 3, 1, 4, 5])
y = array(['f','o','o','b','a','r'])
output = y[x > 1 & x < 5] # desired output is ['o','o','a']

mas isso não funciona. Como eu faria isso?

Prumo
fonte

Respostas:

220

Sua expressão funcionará se você adicionar parênteses:

>>> y[(1 < x) & (x < 5)]
array(['o', 'o', 'a'], 
      dtype='|S1')
jfs
fonte
1
Isso é legal .. vecMask = 1 <x gera uma máscara de vetor como vecMask = (False, True, ...), que pode ser combinada apenas com outras máscaras de vetor. Cada elemento é a condição para obter os elementos de um vetor de origem (True) ou não (False). Isso pode ser usado também com a versão completa numpy.extract (vecMask, vecSrc) ou numpy.where (vecMask, vecSrc, vecSrc2).
MasterControlProgram
6
@JennyYueJin: Isso acontece por causa da precedência. (Bitwise) &tem precedência maior que <e >, que por sua vez tem precedência maior que (lógico) and. x > 1 and x < 5evoca primeiro as desigualdades e depois a conjunção lógica; x > 1 & x < 5avalia a conjunção bit a bit de 1e (os valores em) xe as desigualdades. (x > 1) & (x < 5)força as desigualdades a avaliar primeiro, para que todas as operações ocorram na ordem pretendida e os resultados sejam todos bem definidos. Veja os documentos aqui.
Cevicci
@ ru111 Também funciona no Python 3.6 (não há razão para parar de funcionar).
JFS
Eu recebo "ValueError: o valor de verdade de uma matriz com mais de um elemento é ambíguo. Use a.any () ou a.all ()"
ru111
@ ru111 você deve escrever (0 < x) & (x < 10)(como mostrado na resposta) em vez de 0 < x < 10não funcionar com matrizes numpy em qualquer versão do Python.
JFS
34

O IMO OP realmente não quer np.bitwise_and()(aka &), mas realmente quernp.logical_and() porque eles estão comparando valores lógicos como Truee False- veja esta postagem do SO em lógico versus bit a bit para ver a diferença.

>>> x = array([5, 2, 3, 1, 4, 5])
>>> y = array(['f','o','o','b','a','r'])
>>> output = y[np.logical_and(x > 1, x < 5)] # desired output is ['o','o','a']
>>> output
array(['o', 'o', 'a'],
      dtype='|S1')

E uma maneira equivalente de fazer isso é np.all()definindo o axisargumento adequadamente.

>>> output = y[np.all([x > 1, x < 5], axis=0)] # desired output is ['o','o','a']
>>> output
array(['o', 'o', 'a'],
      dtype='|S1')

pelos números:

>>> %timeit (a < b) & (b < c)
The slowest run took 32.97 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 1.15 µs per loop

>>> %timeit np.logical_and(a < b, b < c)
The slowest run took 32.59 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 1.17 µs per loop

>>> %timeit np.all([a < b, b < c], 0)
The slowest run took 67.47 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 5.06 µs per loop

portanto, usando np.all()é mais lento, mas &e logical_andsão sobre o mesmo.

Mark Mikofski
fonte
7
Você precisa ter um pouco de cuidado com a maneira como fala sobre o que é avaliado. Por exemplo, in output = y[np.logical_and(x > 1, x < 5)], x < 5 é avaliado (possivelmente criando uma matriz enorme), mesmo que seja o segundo argumento, porque essa avaliação ocorre fora da função. IOW, logical_andrecebe dois argumentos já avaliados. Isso é diferente do caso usual de a and b, no qual bnão é avaliado se aé verdadeiro.
DSM
15
não há diferença entre bitwise_and () e logical_and () para matrizes booleanas
jfs
21

Adicione um detalhe às respostas de @JF Sebastian e @Mark Mikofski:
Se alguém deseja obter os índices correspondentes (em vez dos valores reais da matriz), o seguinte código será o seguinte:

Para satisfazer várias (todas) condições:

select_indices = np.where( np.logical_and( x > 1, x < 5) )[0] #   1 < x <5

Para satisfazer várias condições (ou):

select_indices = np.where( np.logical_or( x < 1, x > 5 ) )[0] # x <1 or x >5
Boa vontade
fonte
2
Observe que numpy.where não apenas retornará uma matriz dos índices, mas retornará uma tupla (a saída de condition.nonzero ()) contendo matrizes - nesse caso, (the array of indices you want,)você precisará select_indices = np.where(...)[0]obter o resultado desejado e esperar.
Cevicci
5

Eu gosto de usar np.vectorizepara essas tarefas. Considere o seguinte:

>>> # Arrays
>>> x = np.array([5, 2, 3, 1, 4, 5])
>>> y = np.array(['f','o','o','b','a','r'])

>>> # Function containing the constraints
>>> func = np.vectorize(lambda t: t>1 and t<5)

>>> # Call function on x
>>> y[func(x)]
>>> array(['o', 'o', 'a'], dtype='<U1')

A vantagem é que você pode adicionar muitos outros tipos de restrições na função vetorizada.

Espero que ajude.


fonte
1
Essa não é uma boa maneira de indexar no NumPy (será muito lento).
21819 Alex Riley
1

Na verdade, eu faria assim:

L1 é a lista de índices de elementos que satisfazem a condição 1; (talvez você possa usar somelist.index(condition1)ou np.where(condition1)obter L1.)

Da mesma forma, você obtém L2, uma lista de elementos que satisfazem a condição 2;

Então você encontra a interseção usando intersect(L1,L2).

Você também pode encontrar a interseção de várias listas se tiver várias condições para satisfazer.

Em seguida, você pode aplicar o índice em qualquer outra matriz, por exemplo, x.

Shuo Yang
fonte
0

Para matrizes 2D, você pode fazer isso. Crie uma máscara 2D usando a condição Crie a máscara de condição para int ou float, dependendo da matriz, e multiplique-a pela matriz original.

In [8]: arr
Out[8]: 
array([[ 1.,  2.,  3.,  4.,  5.],
       [ 6.,  7.,  8.,  9., 10.]])

In [9]: arr*(arr % 2 == 0).astype(np.int) 
Out[9]: 
array([[ 0.,  2.,  0.,  4.,  0.],
       [ 6.,  0.,  8.,  0., 10.]])
Gautam Sreekumar
fonte