2010-10-21 15 views
6

Estoy tratando de crear una 'máscara' de numpy.array especificando ciertos criterios. Python tiene incluso agradable sintaxis para algo como esto:Numpy.array indexing question

>> A = numpy.array([1,2,3,4,5]) 
>> A > 3 
array([False, False, False, True, True]) 

Pero si tengo una lista de criterios en lugar de un rango:

>> A = numpy.array([1,2,3,4,5]) 
>> crit = [1,3,5] 

que no puede hacer esto:

>> A in crit 

Tengo que hacer algo basado en la lista de comprensiones, como esta:

>> [a in crit for a in A] 
array([True, False, True, False, True]) 

Cuál es correcto.

Ahora, el problema es que estoy trabajando con grandes matrices y el código anterior es muy lento. ¿Hay una forma más natural de hacer esta operación que pueda acelerarla?

EDIT: Pude obtener una pequeña aceleración haciendo crit en un conjunto.

Edit2: Para aquellos que estén interesados:

enfoque de Jouni: 1000 bucles, lo mejor de 3: 102 mu s por bucle

numpy.in1d: 1.000 bucles, mejor de 3 : 1,33 ms por bucle

Edit3: Apenas probadas de nuevo con B = randint (10, tamaño = 100)

enfoque de Jouni: 1.000 bucles, lo mejor de 3: 2,96 ms por bucle

numpy.in1d: 1.000 bucles, lo mejor de 3: 1,34 ms por cada bucle

Conclusión: Uso numpy. in1d() a menos que B sea muy pequeño.

Respuesta

6

creo que la función numpy in1d es lo que busca:

>>> A = numpy.array([1,2,3,4,5]) 
>>> B = [1,3,5] 
>>> numpy.in1d(A,crit) 
array([ True, False, True, False, True], dtype=bool) 

como se indica en su docstring, "in1d(a, b) es más o menos equivalente a np.array([item in b for item in a])"

Es cierto que no he realizado ninguna prueba de velocidad, pero parece lo que estás buscando.

Otra forma más rápida

Ésta es otra manera de hacerlo que es más rápido. Ordenar la matriz B primero (que contiene los elementos que usted está buscando para encontrar en A), convertirlo en una matriz numpy, y luego hacer:

B[B.searchsorted(A)] == A 

aunque si tiene elementos de A que son más grande que el más grande de B, usted tendrá que hacer:

inds = B.searchsorted(A) 
inds[inds == len(B)] = 0 
mask = B[inds] == A 

puede que no sea más rápido para las pequeñas matrices (especialmente para ser pequeño B), pero al poco tiempo que sin duda será más rápido. ¿Por qué? Debido a que este es un algoritmo O (N log M), donde N es el número de elementos en A y M es el número de elementos en M, armar un grupo de máscaras individuales es O (N * M). Lo probé con N = 10000 y M = 14 y ya era más rápido. De todos modos, solo pensé que le gustaría saber, especialmente si realmente está planeando usar esto en matrices muy grandes.

+0

parece una adición reciente a numpy (no estaba en la versión 1.3) – Paul

+0

Tiene razón. Solo probé en B con una longitud de 3. Si B también es grande, numpy.in1d ​​() definitivamente escala mucho mejor. – aduric

+1

@aduric y mi segundo método es incluso más rápido que in1d. –

0

Crea una máscara y usa la función de compresión de la matriz numpy. Debería ser mucho más rápido. Si tiene un criterio complejo, recuerde construirlo en base a las matemáticas de las matrices.

a = numpy.array([3,1,2,4,5]) 
mask = a > 3 
b = a.compress(mask) 

o

a = numpy.random.random_integers(1,5,100000) 
c=a.compress((a<=4)*(a>=2)) ## numbers between n<=4 and n>=2 
d=a.compress(~((a<=4)*(a>=2))) ## numbers either n>4 or n<2 

Ok, si quieres una máscara que tiene todo a de [1,3,5] se puede hacer algo como

a = numpy.random.random_integers(1,5,100000) 
mask=(a==1)+(a==3)+(a==5) 

o

a = numpy.random.random_integers(1,5,100000) 
mask = numpy.zeros(len(a), dtype=bool) 
for num in [1,3,5]: 
    mask += (a==num) 
+0

No creo que esto sea lo que estoy buscando. No quiero recuperar el contenido real de la matriz, solo quiero obtener una máscara booleana que tenga la misma longitud que la matriz original. – aduric

+0

Ok, lo edité ahora que sé lo que quieres. Supongo que la solución de Jouni que se le ocurrió mientras estaba editando la mía fue equivalente, como True = True + True, True = True + False, False = False + False, exactamente igual que o usando |. –

3

Combina varias comparaciones con "o":

A = randint(10,size=10000) 
mask = (A == 1) | (A == 3) | (A == 5) 

O si usted tiene una lista B y desea crear la máscara de forma dinámica:

B = [1, 3, 5] 
mask = zeros((10000,),dtype=bool) 
for t in B: mask = mask | (A == t) 
+0

preguntándome por qué o cómo anticipar cuándo 'numpy' hará de forma natural que' ufunc' habilite la operación lógica de elemento? Cuando se realizan operaciones lógicas 'numpy' a veces devuelve una excepción:' ValueError: el valor de verdad de una matriz con más de un elemento es ambiguo. Use a .any() o a.all(). ' – dtlussier

+0

este es sin duda el enfoque más rápido, aunque no el más limpio. – aduric