Domanda

Quindi, supponiamo che io abbia 100.000 array float con 100 elementi ciascuno. Ho bisogno del più alto numero X di valori, MA solo se sono maggiori di Y. Qualsiasi 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 è già impostata su 0.

variabili di esempio:

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

risultato atteso:

array = [0, .25, 0, .15, .5, 0, 0, 0, 0, 0]
È stato utile?

Soluzione

Questo è un tipico lavoro per NumPy , che è molto veloce per questo tipo 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 hai solo bisogno degli elementi più grandi di highCountX, puoi persino " dimenticare " gli elementi piccoli (anziché impostarli su 0 e ordinarli) e ordinare solo l'elenco di elementi di grandi dimensioni:

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

Ovviamente, l'ordinamento dell'intero array se sono necessari solo alcuni elementi potrebbe non essere ottimale. A seconda delle tue esigenze, potresti prendere in considerazione il modulo heapq standard.

Altri suggerimenti

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

:)

Esiste una speciale classe MaskedArray in NumPy che fa esattamente questo. Puoi & Quot; maschera & Quot; elementi basati su qualsiasi condizione preliminare. Ciò rappresenta meglio la tua necessità rispetto all'assegnazione di zeri: le operazioni intorpidite ignoreranno i valori mascherati quando appropriato (ad esempio, la ricerca di 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 vantaggio aggiuntivo, gli array mascherati sono ben supportati nella libreria di visualizzazione matplotlib se ne hai bisogno.

Documenti su array mascherati in numpy

Uso di 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 scritta senza <=> come segue:

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

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, seleziona tutti gli elementi maggiori di lowValY:

[x for x in array if x > lowValY]

Questo array contiene solo il numero di elementi maggiore della soglia. Quindi, ordinandolo in modo che i valori più grandi siano all'inizio:

sorted(..., reverse=True)

Quindi un indice di elenco prende la soglia per i primi highCountX elementi:

sorted(...)[highCountX-1]

Infine, l'array originale viene compilato usando un'altra comprensione dell'elenco:

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

Esiste una condizione al contorno in cui vi sono due o più elementi uguali che (nel tuo esempio) sono il 3 ° elemento più alto. La matrice risultante conterrà quell'elemento più di una volta.

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

Gli elementi delle impostazioni sotto una certa soglia a zero sono facili:

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

(più gli addominali occasionali () se necessario.)

Il requisito degli N numeri più alti è comunque un po 'vago. E se ci fossero ad es. N + 1 numeri uguali sopra la soglia? Quale troncare?

È possibile ordinare prima l'array, quindi impostare la soglia sul valore dell'ennesimo elemento:

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

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

Puoi usare map e lambda, dovrebbe essere abbastanza veloce.

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

Utilizza un heap .

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 funziona nell'heap O(lg(k)) e nell'inserimento O(1) o <=> a seconda del tipo di heap che usi.

Usare un heap è una buona idea, come dice egon. Ma puoi usare la funzione heapq.nlargest per ridurre alcuni sforzi:

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]
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top