2009-10-26 16 views
34

Quindi, diciamo che ho 100.000 array float con 100 elementi ciascuno. Ho bisogno del massimo numero X di valori, MA solo se sono maggiori di Y. Ogni elemento che non corrisponde a questo dovrebbe essere impostato su 0. Quale sarebbe il modo più veloce per farlo in Python? L'ordine deve essere mantenuto. La maggior parte degli elementi sono già impostato a 0.Il modo più veloce per azzerare i valori bassi dell'array?

variabili del campione: risultato

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

previsto:

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

Qual è HightCountX è per? –

+0

highCountX è il numero massimo di elementi diversi da zero che desidero esistere nell'array – David

+0

Se era 2 il risultato previsto sarebbe: [0, 0, 0, .15, .5, 0, 0, 0, 0, 0] - highCountX limita il numero di elementi diversi da zero nel risultato. – Abgan

risposta

73

Questo è un lavoro tipico per NumPy, che è molto veloce per questi tipi di operazioni:

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 

Ora, se avete solo bisogno gli highCountX più grandi elementi, si può anche "dimenticare" i piccoli elementi (invece di impostare a 0 e lo smistamento) e solo sorta l'elenco dei grandi elementi:

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

Naturalmente, l'ordinamento l'intera matrice se hai solo bisogno di alcuni elementi potrebbero non essere ottimale. A seconda delle esigenze, potresti prendere in considerazione il modulo standard heapq.

+5

Bello ... usare le librerie appropriate può portarti molto lontano :-) – Abgan

+0

Continuo a imbattersi in questo numPy, credo che dovrò verificarlo :) Grazie per l'aiuto (tutti). – David

+0

@David NumPy soddisfa davvero un bisogno. Ti suggerisco di iniziare con il tutorial a cui mi sono collegato: è probabilmente il modo più veloce per imparare a usare NumPy e imparare i concetti più importanti. – EOL

5

Il modo più semplice sarebbe:

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] 

In pezzi, questo seleziona tutti gli elementi maggiori di lowValY:

[x for x in array if x > lowValY] 

Questo array contiene solo il numero di elementi superiori alla soglia. Poi, smistamento così i valori più grandi sono in partenza:

sorted(..., reverse=True) 

quindi un indice elenco calcia la soglia per i primi highCountX elementi:

sorted(...)[highCountX-1] 

Infine, l'array originale viene compilato utilizzando un altro di lista:

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

c'è una condizione al contorno in cui ci sono due o più elementi uguali che (nel tuo esempio) sono 3rd elementi più alti. La matrice risultante conterrà quell'elemento più di una volta.

Esistono anche altre condizioni al contorno, ad esempio len(array) < highCountX. La gestione di tali condizioni è lasciata all'implementatore.

+1

È possibile utilizzare x per x nell'array se x> lowValY anziché [x per x nell'array se x> lowValY] per enumerare semplicemente l'array originale senza copiarlo (se i dati originali sono abbastanza grandi, questa potrebbe essere una buona cosa da fare). – Abgan

+1

È vero. 'sorted()' probabilmente avrà comunque bisogno dell'intera lista, comunque. –

+0

Heh, 3 volte più veloce del mio codice noob, ma avrei bisogno degli elementi uguali per mantenere il limite highCountX. Gli array dovrebbero avere da 20-200 elementi ... sono in realtà segmenti di un array più grande che elaboriamo in blocchi. Grazie per l'aiuto finora. – David

2

Impostazioni elementi sotto una certa soglia a zero è facile: (. Più l'abs occasionale() se necessario)

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

Il requisito dei numeri elevati N è un po 'vago, tuttavia. Cosa succede se ci sono, ad es. N + 1 numeri uguali sopra la soglia? Quale troncare?

è possibile ordinare l'array, quindi impostare la soglia al valore dell'elemento Nth:

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

Nota: questa soluzione è ottimizzata per leggibilità non le prestazioni.

+0

in questo caso, non importa quale viene troncato ... più importante è che highCountX è seguito – David

6

Utilizzando 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<x] = 0 #NOTE: it might leave more than highCountX non-zero elements 
      # . if there are duplicates 

Dove partial_sort potrebbe essere:

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] 

L'espressione a[a<value] = 0 può essere scritto senza numpy come segue:

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

È possibile utilizzare carta e lambda , dovrebbe essere veloce e nough.

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

Utilizzare un heap.

Questo funziona in tempo 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 lavora in mucchio O(lg(k)) e l'inserimento O(lg(k)) o O(1) a seconda del tipo heap si utilizza.

+0

non ha verificato la sintassi del codice ... – Egon

7

C'è una classe MaskedArray speciale in NumPy che fa esattamente questo. Puoi "mascherare" gli elementi in base a qualsiasi precondizione. Rappresenta meglio le tue necessità rispetto all'assegnazione degli zeri: le operazioni di numpy ignoreranno i valori mascherati quando appropriato (ad esempio, per trovare il valore 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 ] 

Come beneficio addded, gli array mascherati sono ben supportate nella libreria di visualizzazione matplotlib se avete bisogno di questo.

Docs on masked arrays in numpy

0

Utilizzando un mucchio è una buona idea, come dice Egon. Ma è possibile utilizzare la funzione heapq.nlargest per ridurre un certo sforzo:

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] 
+0

Mi piace questa soluzione fatta in casa che utilizza solo moduli standard. Tuttavia, dovrebbe essere aggiornato in modo da restituire realmente gli elementi highCountX più grandi (se molti elementi nella matrice hanno valore 'threshold', l'array finale ha troppi elementi diversi da zero). – EOL

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

:)

Problemi correlati