Filtro de umbral eficiente de una matriz con numpy

81

Necesito filtrar una matriz para eliminar los elementos que están por debajo de un cierto umbral. Mi código actual es así:

threshold = 5
a = numpy.array(range(10)) # testing data
b = numpy.array(filter(lambda x: x >= threshold, a))

El problema es que esto crea una lista temporal, usando un filtro con función lambda (lento).

Como esta es una operación bastante simple, tal vez haya una función numerosa que lo haga de manera eficiente, pero no he podido encontrarla.

Pensé que otra forma de lograr esto podría ser ordenar la matriz, encontrar el índice del umbral y devolver una porción de ese índice en adelante, pero incluso si esto sería más rápido para entradas pequeñas (y no se notará de todos modos ), definitivamente es asintóticamente menos eficiente a medida que aumenta el tamaño de entrada.

¿Algunas ideas? ¡Gracias!

Actualización : también tomé algunas medidas, y la clasificación y el corte seguían siendo dos veces más rápido que el filtro de Python puro cuando la entrada era 100.000.000 de entradas.

In [321]: r = numpy.random.uniform(0, 1, 100000000)

In [322]: %timeit test1(r) # filter
1 loops, best of 3: 21.3 s per loop

In [323]: %timeit test2(r) # sort and slice
1 loops, best of 3: 11.1 s per loop

In [324]: %timeit test3(r) # boolean indexing
1 loops, best of 3: 1.26 s per loop
fortran
fuente
2
sí, es bastante agradable :-) incluso calcula automáticamente el número de iteraciones se debe realizar en un promedio de las mediciones si el código lleva muy poco tiempo para ejecutar
FORTRAN
5
@yosukesabai - IPython's %timeitusa el timeitmódulo incorporado . Échale un vistazo también. docs.python.org/library/timeit.html
Joe Kington

Respuestas:

112

b = a[a>threshold] esto debería hacer

Probé de la siguiente manera:

import numpy as np, datetime
# array of zeros and ones interleaved
lrg = np.arange(2).reshape((2,-1)).repeat(1000000,-1).flatten()

t0 = datetime.datetime.now()
flt = lrg[lrg==0]
print datetime.datetime.now() - t0

t0 = datetime.datetime.now()
flt = np.array(filter(lambda x:x==0, lrg))
print datetime.datetime.now() - t0

tengo

$ python test.py
0:00:00.028000
0:00:02.461000

http://docs.scipy.org/doc/numpy/user/basics.indexing.html#boolean-or-mask-index-arrays

yosukesabai
fuente
1
resultado de la prueba agregado, no solo lo que creo que debería hacer. : p
yosukesabai
3
Este tipo de indexación no mantiene el tamaño de la matriz, ¿cómo es posible mantener el mismo número de elementos y poner a cero los valores del subumbral?
linello
9
@linello, un [a <= umbral] = 0 va a enmascarar la parte que no excede el umbral
yosukesabai
4
Me encontré con el problema del filtrado basado en dos criterios. Aquí está la solución: stackoverflow.com/a/3248599/1373468
Robin Newhouse
@yosukesabai ¿Es posible hacer exactamente esto, sin cambiar los valores originales? Si np.maestá destinado a hacer eso, no sé cómo.
embert