Numpy onde funções múltiplas condições

132

Eu tenho uma variedade de distâncias chamadas dists. Quero selecionar dists que estão entre dois valores. Eu escrevi a seguinte linha de código para fazer isso:

 dists[(np.where(dists >= r)) and (np.where(dists <= r + dr))]

No entanto, isso seleciona apenas para a condição

 (np.where(dists <= r + dr))

Se eu executar os comandos sequencialmente usando uma variável temporária, ele funcionará bem. Por que o código acima não funciona e como faço para funcionar?

Felicidades

user1654183
fonte

Respostas:

203

A melhor maneira no seu caso específico seria apenas alterar seus dois critérios para um critério:

dists[abs(dists - r - dr/2.) <= dr/2.]

Ele cria apenas uma matriz booleana e, na minha opinião, é mais fácil de ler porque diz: está distdentro de um drou r? (Embora eu redefinisse rpara ser o centro da sua região de interesse e não o começo, então r = r + dr/2.) Mas isso não responde à sua pergunta.


A resposta para sua pergunta:
você realmente não precisa wherese estiver apenas tentando filtrar os elementos distsque não atendem aos seus critérios:

dists[(dists >= r) & (dists <= r+dr)]

Porque o &mostrará um elemento and(os parênteses são necessários).

Ou, se você deseja usar wherepor algum motivo, pode:

 dists[(np.where((dists >= r) & (dists <= r + dr)))]

Motivo:
o motivo pelo qual não funciona é porque np.whereretorna uma lista de índices, não uma matriz booleana. Você está tentando obter andentre duas listas de números, que obviamente não têm os valores True/ Falseesperados. Se ae bsão os dois Truevalores, então a and bretorna b. Então, dizer algo como [0,1,2] and [2,3,4]apenas lhe dará [2,3,4]. Aqui está em ação:

In [230]: dists = np.arange(0,10,.5)
In [231]: r = 5
In [232]: dr = 1

In [233]: np.where(dists >= r)
Out[233]: (array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]),)

In [234]: np.where(dists <= r+dr)
Out[234]: (array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12]),)

In [235]: np.where(dists >= r) and np.where(dists <= r+dr)
Out[235]: (array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12]),)

O que você esperava comparar era simplesmente a matriz booleana, por exemplo

In [236]: dists >= r
Out[236]: 
array([False, False, False, False, False, False, False, False, False,
       False,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True], dtype=bool)

In [237]: dists <= r + dr
Out[237]: 
array([ True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True, False, False, False, False, False,
       False, False], dtype=bool)

In [238]: (dists >= r) & (dists <= r + dr)
Out[238]: 
array([False, False, False, False, False, False, False, False, False,
       False,  True,  True,  True, False, False, False, False, False,
       False, False], dtype=bool)

Agora você pode chamar np.wherea matriz booleana combinada:

In [239]: np.where((dists >= r) & (dists <= r + dr))
Out[239]: (array([10, 11, 12]),)

In [240]: dists[np.where((dists >= r) & (dists <= r + dr))]
Out[240]: array([ 5. ,  5.5,  6. ])

Ou simplesmente indexe a matriz original com a matriz booleana usando indexação sofisticada

In [241]: dists[(dists >= r) & (dists <= r + dr)]
Out[241]: array([ 5. ,  5.5,  6. ])
askewchan
fonte
61

A resposta aceita explicou o problema suficientemente bem. No entanto, a abordagem mais numpythonic para aplicar várias condições é usar funções lógicas numpy . Neste ase você pode usar np.logical_and:

np.where(np.logical_and(np.greater_equal(dists,r),np.greater_equal(dists,r + dr)))
Kasramvd
fonte
11

Uma coisa interessante a apontar aqui; a maneira usual de usar OR e AND também funcionará neste caso, mas com uma pequena alteração. Em vez de "e" e em vez de "ou", use Ampersand (&) e Pipe Operator (|) e ele funcionará.

Quando usamos 'e' :

ar = np.array([3,4,5,14,2,4,3,7])
np.where((ar>3) and (ar<6), 'yo', ar)

Output:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Quando usamos o E comercial (&) :

ar = np.array([3,4,5,14,2,4,3,7])
np.where((ar>3) & (ar<6), 'yo', ar)

Output:
array(['3', 'yo', 'yo', '14', '2', 'yo', '3', '7'], dtype='<U11')

E isso é o mesmo quando estamos tentando aplicar vários filtros no caso do panda Dataframe. Agora, o raciocínio por trás disso tem a ver com operadores lógicos e operadores bit a bit e, para obter mais informações sobre o mesmo, sugiro passar por essa resposta ou por uma pergunta / pergunta semelhante no fluxo de pilha.

ATUALIZAR

Um usuário perguntou: por que é necessário dar (ar> 3) e (ar <6) entre parênteses? Bem, aqui está a coisa. Antes de começar a falar sobre o que está acontecendo aqui, é preciso saber sobre a precedência do Operador no Python.

Semelhante ao que é o BODMAS, o python também dá precedência ao que deve ser executado primeiro. Os itens entre parênteses são executados primeiro e, em seguida, o operador bit a bit começa a funcionar. Vou mostrar abaixo o que acontece nos dois casos em que você usa e não usa "(", ")".

Caso 1:

np.where( ar>3 & ar<6, 'yo', ar)
np.where( np.array([3,4,5,14,2,4,3,7])>3 & np.array([3,4,5,14,2,4,3,7])<6, 'yo', ar)

Como não existem suportes Aqui, o operador bit a bit ( &) está ficando confuso aqui que o que você está mesmo pedindo-lhe para obter E lógico de, porque na tabela de precedência de operadores, se você ver, &é dada precedência sobre <ou >operadores. Aqui está a tabela da precedência mais baixa à mais alta.

insira a descrição da imagem aqui

Não é mesmo realizando o <e >operação e sendo solicitado a executar uma operação lógica AND. É por isso que dá esse erro.

Pode-se verificar o seguinte link para saber mais sobre: precedência do operador

Agora para o caso 2:

Se você usar o suporte, verá claramente o que acontece.

np.where( (ar>3) & (ar<6), 'yo', ar)
np.where( (array([False,  True,  True,  True, False,  True, False,  True])) & (array([ True,  True,  True, False,  True,  True,  True, False])), 'yo', ar)

Duas matrizes de Verdadeiro e Falso. E você pode facilmente executar operações AND lógicas neles. O que lhe dá:

np.where( array([False,  True,  True, False, False,  True, False, False]),  'yo', ar)

E descanse, sabe, np.where, para casos específicos, onde True, atribui o primeiro valor (ou seja, aqui 'yo') e, se False, o outro (ou seja, aqui, mantendo o original).

Isso é tudo. Espero ter explicado bem a consulta.

Amit Amola
fonte
1
Por que você tem que dar a ()volta (ar>3)e (ar>6)?
RTrain3k 30/01
Essa é uma pergunta muito boa. É uma pergunta tão boa que eu tive que pensar que o que diabos é a necessidade disso. Por isso, perguntei a um colega também e discutimos agora que tenho uma solução para você. Colocando na resposta como uma atualização. É realmente simples, mas uma coisa difícil de entender realmente.
Amit Amola 31/01
Finalize a atualização RTrain3k, respondi sua consulta.
Amit Amola 31/01
5

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

>>> # function which returns True when constraints are satisfied.
>>> func = lambda d: d >= r and d<= (r+dr) 
>>>
>>> # Apply constraints element-wise to the dists array.
>>> result = np.vectorize(func)(dists) 
>>>
>>> result = np.where(result) # Get output.

Você também pode usar em np.argwherevez de np.whereobter uma saída clara. Mas essa é a sua decisão :)

Espero que ajude.


fonte
2

Experimentar:

np.intersect1d(np.where(dists >= r)[0],np.where(dists <= r + dr)[0])
Xin Wang
fonte
2

Isso deve funcionar:

dists[((dists >= r) & (dists <= r+dr))]

A maneira mais elegante ~~

Qhan
fonte
2

Experimentar:

import numpy as np
dist = np.array([1,2,3,4,5])
r = 2
dr = 3
np.where(np.logical_and(dist> r, dist<=r+dr))

Saída: (matriz ([2, 3]))

Você pode ver as funções lógicas para mais detalhes.

Xiong-Hui Chen
fonte
0

Eu elaborei este exemplo simples

import numpy as np

ar = np.array([3,4,5,14,2,4,3,7])

print [X for X in list(ar) if (X >= 3 and X <= 6)]

>>> 
[3, 4, 5, 4, 3]
Kiriloff
fonte
6
Não há necessidade de iterar neste caso. NumPy possui indexação booleana.
M456