Numpy: encuentra el índice de los elementos dentro del rango

85

Tengo una gran variedad de números, por ejemplo,

a = np.array([1, 3, 5, 6, 9, 10, 14, 15, 56])  

Me gustaría encontrar todos los índices de los elementos dentro de un rango específico. Por ejemplo, si el rango es (6, 10), la respuesta debería ser (3, 4, 5). ¿Hay una función incorporada para hacer esto?

Beto
fuente

Respuestas:

140

Puede usar np.wherepara obtener índices y np.logical_andestablecer dos condiciones:

import numpy as np
a = np.array([1, 3, 5, 6, 9, 10, 14, 15, 56])

np.where(np.logical_and(a>=6, a<=10))
# returns (array([3, 4, 5]),)
deinonychusaur
fuente
6
Por cierto, lo mismo se logra con np.nonzero(np.logical_and(a>=6, a<=10)).
3lectrologos 13/12/12
10
También np.where((a > 6) & (a <= 10))
ELinda
no parece funcionar bien con matrices multidimensionales
Monica Heddneck
1
@ELinda np.logical_andes un poco más rápido que eso &. Y np.wherees más rápido que np.nonzero.
Skillmon le gusta topanswers.xyz
Tiene un rendimiento muy malo para arreglos más grandes
EZLearner
62

Como en la respuesta de @ deinonychusaur, pero aún más compacta:

In [7]: np.where((a >= 6) & (a <=10))
Out[7]: (array([3, 4, 5]),)
tiago
fuente
19
Agradable. También puede hacerlo a[(a >= 6) & (a <= 10)]si aes una matriz numpy.
ws_e_c421
1
En caso de que alguien se confunda como yo con la redacción del comentario: esto no funciona para listas ordinarias, es solo si aes una matriz numpy
Prof
14

Pensé que agregaría esto porque aen el ejemplo que dio está ordenado:

import numpy as np
a = [1, 3, 5, 6, 9, 10, 14, 15, 56] 
start = np.searchsorted(a, 6, 'left')
end = np.searchsorted(a, 10, 'right')
rng = np.arange(start, end)
rng
# array([3, 4, 5])
Bi Rico
fuente
13
a = np.array([1,2,3,4,5,6,7,8,9])
b = a[(a>2) & (a<8)]
Abhishek
fuente
6

Resumen de las respuestas

Para comprender cuál es la mejor respuesta, podemos calcular el tiempo utilizando la solución diferente. Desafortunadamente, la pregunta no estaba bien planteada por lo que hay respuestas a diferentes preguntas, aquí trato de apuntar la respuesta a la misma pregunta. Dada la matriz:

a = np.array([1, 3, 5, 6, 9, 10, 14, 15, 56])

La respuesta deben ser los índices de los elementos entre un cierto rango, asumimos inclusivo, en este caso, 6 y 10.

answer = (3, 4, 5)

Correspondiente a los valores 6,9,10.

Para probar la mejor respuesta, podemos usar este código.

import timeit
setup = """
import numpy as np
import numexpr as ne

a = np.array([1, 3, 5, 6, 9, 10, 14, 15, 56])
# we define the left and right limit
ll = 6
rl = 10

def sorted_slice(a,l,r):
    start = np.searchsorted(a, l, 'left')
    end = np.searchsorted(a, r, 'right')
    return np.arange(start,end)
"""

functions = ['sorted_slice(a,ll,rl)', # works only for sorted values
'np.where(np.logical_and(a>=ll, a<=rl))[0]',
'np.where((a >= ll) & (a <=rl))[0]',
'np.where((a>=ll)*(a<=rl))[0]',
'np.where(np.vectorize(lambda x: ll <= x <= rl)(a))[0]',
'np.argwhere((a>=ll) & (a<=rl)).T[0]', # we traspose for getting a single row
'np.where(ne.evaluate("(ll <= a) & (a <= rl)"))[0]',]

functions2 = [
   'a[np.logical_and(a>=ll, a<=rl)]',
   'a[(a>=ll) & (a<=rl)]',
   'a[(a>=ll)*(a<=rl)]',
   'a[np.vectorize(lambda x: ll <= x <= rl)(a)]',
   'a[ne.evaluate("(ll <= a) & (a <= rl)")]',
]

Resultados

Los resultados se informan en la siguiente gráfica. En la parte superior las soluciones más rápidas. ingrese la descripción de la imagen aquí Si en lugar de los índices quieres extraer los valores puedes realizar las pruebas usando functions2 pero los resultados son casi los mismos.

GM
fuente
Estos resultados solo se aplican a una matriz de longitud específica (aquí eligió una matriz muy pequeña). Estos resultados cambian rápidamente para arreglos más grandes
EZLearner
4

Este fragmento de código devuelve todos los números en una matriz numerosa entre dos valores:

a = np.array([1, 3, 5, 6, 9, 10, 14, 15, 56] )
a[(a>6)*(a<10)]

Funciona de la siguiente manera: (a> 6) devuelve una matriz numerosa con Verdadero (1) y Falso (0), también lo hace (a <10). Al multiplicar estos dos juntos, se obtiene una matriz con Verdadero, si ambas declaraciones son Verdaderas (porque 1x1 = 1) o Falsa (porque 0x0 = 0 y 1x0 = 0).

La parte a [...] devuelve todos los valores de la matriz a donde la matriz entre corchetes devuelve una declaración Verdadera.

Por supuesto, puede hacer esto más complicado diciendo, por ejemplo

...*(1-a<10) 

que es similar a una declaración "y no".

Nathan
fuente
1
a = np.array([1, 3, 5, 6, 9, 10, 14, 15, 56])
np.argwhere((a>=6) & (a<=10))
Abhishek
fuente
1

Quería agregar numexpr a la mezcla:

import numpy as np
import numexpr as ne

a = np.array([1, 3, 5, 6, 9, 10, 14, 15, 56])  

np.where(ne.evaluate("(6 <= a) & (a <= 10)"))[0]
# array([3, 4, 5], dtype=int64)

Solo tendría sentido para matrices más grandes con millones ... o si alcanza los límites de memoria.

k1m190r
fuente
1

Otra forma es con:

np.vectorize(lambda x: 6 <= x <= 10)(a)

que devuelve:

array([False, False, False,  True,  True,  True, False, False, False])

A veces es útil para enmascarar series de tiempo, vectores, etc.

Jorge Crvz
fuente
0
s=[52, 33, 70, 39, 57, 59, 7, 2, 46, 69, 11, 74, 58, 60, 63, 43, 75, 92, 65, 19, 1, 79, 22, 38, 26, 3, 66, 88, 9, 15, 28, 44, 67, 87, 21, 49, 85, 32, 89, 77, 47, 93, 35, 12, 73, 76, 50, 45, 5, 29, 97, 94, 95, 56, 48, 71, 54, 55, 51, 23, 84, 80, 62, 30, 13, 34]

dic={}

for i in range(0,len(s),10):
    dic[i,i+10]=list(filter(lambda x:((x>=i)&(x<i+10)),s))
print(dic)

for keys,values in dic.items():
    print(keys)
    print(values)

Salida:

(0, 10)
[7, 2, 1, 3, 9, 5]
(20, 30)
[22, 26, 28, 21, 29, 23]
(30, 40)
[33, 39, 38, 32, 35, 30, 34]
(10, 20)
[11, 19, 15, 12, 13]
(40, 50)
[46, 43, 44, 49, 47, 45, 48]
(60, 70)
[69, 60, 63, 65, 66, 67, 62]
(50, 60)
[52, 57, 59, 58, 50, 56, 54, 55, 51]  
Vigneshwaran Narayanan
fuente
0

Puede que este no sea el más bonito, pero funciona para cualquier dimensión.

a = np.array([[-1,2], [1,5], [6,7], [5,2], [3,4], [0, 0], [-1,-1]])
ranges = (0,4), (0,4) 

def conditionRange(X : np.ndarray, ranges : list) -> np.ndarray:
    idx = set()
    for column, r in enumerate(ranges):
        tmp = np.where(np.logical_and(X[:, column] >= r[0], X[:, column] <= r[1]))[0]
        if idx:
            idx = idx & set(tmp)
        else:
            idx = set(tmp)
    idx = np.array(list(idx))
    return X[idx, :]

b = conditionRange(a, ranges)
print(b)
cvanelteren
fuente
-4

Puede usar np.clip()para lograr lo mismo:

a = [1, 3, 5, 6, 9, 10, 14, 15, 56]  
np.clip(a,6,10)

Sin embargo, mantiene los valores menores y mayores que 6 y 10 respectivamente.

Deba Pratim Saha
fuente