¿La forma más rápida de poner a cero los valores bajos en la matriz?

Entonces, digamos que tengo 100,000 arreglos flotantes con 100 elementos cada uno. Necesito el número X más alto de valores, PERO solo si son mayores que Y. Cualquier elemento que no coincida con esto debe establecerse en 0. ¿Cuál sería la forma más rápida de hacerlo en Python? El orden debe mantenerse. La mayoría de los elementos ya están establecidos en 0.

variables de muestra:

array = [.06, .25, 0, .15, .5, 0, 0, 0.04, 0, 0] highCountX = 3 lowValY = .1 

Resultado Esperado:

 array = [0, .25, 0, .15, .5, 0, 0, 0, 0, 0] 

Este es un trabajo típico para NumPy , que es muy rápido para este tipo de operaciones:

 array_np = numpy.asarray(array) low_values_flags = array_np < lowValY # Where values are low array_np[low_values_flags] = 0 # All low values set to 0 

Ahora, si solo necesita los elementos más grandes de highCountX, incluso puede "olvidar" los elementos pequeños (en lugar de establecerlos en 0 y ordenarlos) y solo ordenar la lista de elementos grandes:

 array_np = numpy.asarray(array) print numpy.sort(array_np[array_np >= lowValY])[-highCountX:] 

Por supuesto, clasificar la matriz completa si solo necesita algunos elementos puede no ser óptimo. Dependiendo de sus necesidades, es posible que desee considerar el módulo estándar heapq .

 from scipy.stats import threshold thresholded = threshold(array, 0.5) 

🙂

Hay una clase especial de MaskedArray en NumPy que hace exactamente eso. Puede “enmascarar” elementos según cualquier condición previa. Esto representa mejor su necesidad que la asignación de ceros: las operaciones de números ignorarán los valores enmascarados cuando sea apropiado (por ejemplo, encontrar el valor medio).

 >>> from numpy import ma >>> x = ma.array([.06, .25, 0, .15, .5, 0, 0, 0.04, 0, 0]) >>> x1 = ma.masked_inside(0, 0.1) # mask everything in 0..0.1 range >>> x1 masked_array(data = [-- 0.25 -- 0.15 0.5 -- -- -- -- --], mask = [ True False True False False True True True True True], fill_value = 1e+20) >>> print x.filled(0) # Fill with zeroes [ 0 0.25 0 0.15 0.5 0 0 0 0 0 ] 

Como beneficio adicional, las matrices enmascaradas se admiten en la biblioteca de visualización matplotlib si lo necesita.

Docs en arreglos enmascarados en numpy

Utilizando numpy :

 # assign zero to all elements less than or equal to `lowValY` a[a<=lowValY] = 0 # find n-th largest element in the array (where n=highCountX) x = partial_sort(a, highCountX, reverse=True)[:highCountX][-1] # a[a 

Donde partial_sort podría ser:

 def partial_sort(a, n, reverse=False): #NOTE: in general it should return full list but in your case this will do return sorted(a, reverse=reverse)[:n] 

La expresión a[a se puede escribir sin numpy siguiente manera:

 for i, x in enumerate(a): if x < value: a[i] = 0 

La forma más sencilla sería:

 topX = sorted([x for x in array if x > lowValY], reverse=True)[highCountX-1] print [x if x >= topX else 0 for x in array] 

En piezas, esto selecciona todos los elementos mayores que lowValY :

 [x for x in array if x > lowValY] 

Esta matriz solo contiene el número de elementos mayor que el umbral. Luego, ordenándolo para que los valores más grandes estén al principio:

 sorted(..., reverse=True) 

Luego, un índice de lista toma el umbral para los elementos superiores de highCountX :

 sorted(...)[highCountX-1] 

Finalmente, la matriz original se completa utilizando otra lista de comprensión:

 [x if x >= topX else 0 for x in array] 

Hay una condición de límite en la que hay dos o más elementos iguales que (en su ejemplo) son los elementos más altos en tercer lugar. La matriz resultante contendrá ese elemento más de una vez.

También hay otras condiciones de contorno, como si len(array) < highCountX . El manejo de tales condiciones se deja al implementador.

Configuración de elementos por debajo de algún umbral a cero es fácil:

 array = [ x if x > threshold else 0.0 for x in array ] 

(más los abdominales ocasionales () si es necesario).

Sin embargo, el requisito de los N números más altos es un poco vago. ¿Qué pasa si hay, por ejemplo, N + 1 números iguales por encima del umbral? ¿Cuál truncar?

Puede ordenar la matriz primero y luego establecer el umbral al valor del elemento Nth:

 threshold = sorted(array, reverse=True)[N] array = [ x if x >= threshold else 0.0 for x in array ] 

Nota: esta solución está optimizada para facilitar la lectura, no el rendimiento.

Puedes usar map y lambda, debería ser lo suficientemente rápido.

 new_array = map(lambda x: x if x>y else 0, array) 

Utilice un montón .

Esto funciona en el tiempo O(n*lg(HighCountX)) .

 import heapq heap = [] array = [.06, .25, 0, .15, .5, 0, 0, 0.04, 0, 0] highCountX = 3 lowValY = .1 for i in range(1,highCountX): heappush(heap, lowValY) heappop(heap) for i in range( 0, len(array) - 1) if array[i] > heap[0]: heappush(heap, array[i]) min = heap[0] array = [x if x >= min else 0 for x in array] 

Deletemin funciona en el montón O(lg(k)) y en la inserción O(lg(k)) o en O(1) según el tipo de montón que utilice.

Usar una stack es una buena idea, como dice egon. Pero puede usar la función heapq.nlargest para reducir algunos esfuerzos:

 import heapq array = [.06, .25, 0, .15, .5, 0, 0, 0.04, 0, 0] highCountX = 3 lowValY = .1 threshold = max(heapq.nlargest(highCountX, array)[-1], lowValY) array = [x if x >= threshold else 0 for x in array]