2015-02-17 26 views
14

Ho una matrice 1D di valori che dovrebbe essere monotona (diciamo decrescente), ma ci sono regioni casuali in cui il valore aumenta con l'indice.Creare un array monotonico numpy senza un loop Python

Ho bisogno di un array in cui ogni regione viene sostituita con un valore direttamente precedente, in modo che l'array risultante sia ordinato.

Quindi, se dato array è:

a = np.array([10.0, 9.5, 8.0, 7.2, 7.8, 8.0, 7.0, 5.0, 3.0, 2.5, 3.0, 2.0]) 

Voglio che il risultato sia

b = np.array([10.0, 9.5, 8.0, 7.2, 7.2, 7.2, 7.0, 5.0, 3.0, 2.5, 2.5, 2.0]) 

Ecco una rappresentazione grafica:

example

so come raggiungerlo con un loop Python, ma c'è un modo per farlo con NumPy macchinari?

codice Python per chiarezza:

b = np.array(a) 
for i in range(1, b.size): 
    if b[i] > b[i-1]: 
     b[i] = b[i-1] 
+0

Perché la preoccupazione per "senza loop"? Sia che si scriva un ciclo esplicito, sia che il ciclo venga eseguito in una funzione importata da qualche modulo/pacchetto, è ancora lì. Non ci sono molti modi per fare qualcosa a una serie di valori che non implicano un ciclo, a meno che non si desideri srotolare completamente l'intero ciclo in una sequenza lineare di operazioni, il che è brutto per diversi motivi: portabilità, flessibilità , dimensione del codice, ecc ... – twalberg

+0

@twalberg Penso che sia comune cercare di evitare i loop Python quando si usa NumPy, perché le prestazioni generalmente migliorano se l'iterazione viene eseguita all'interno delle funzioni implementate in C. Capita anche spesso che il codice sia pulitore più corto. –

+0

Questo è un punto valido quando si lavora con insiemi di dati di grandi dimensioni. Tuttavia, in questo esempio (e senza alcuna indicazione che il problema "reale" è più grande di ordini di grandezza), penso che il sovraccarico di convertire l'elenco Python in una struttura dati su cui il ciclo C può funzionare, e quindi riconvertirlo in la corretta struttura dati Python, probabilmente annulla qualsiasi potenziale guadagno non solo usando un loop Python per iterare oltre una dozzina di voci ... Meglio convalidare che il loop è un problema prima di tentare solo ciecamente di eliminarlo ... – twalberg

risposta

16

È possibile utilizzare np.minimum.accumulate per raccogliere i valori minimi come ci si sposta attraverso l'array:

>>> np.minimum.accumulate(a) 
array([ 10. , 9.5, 8. , 7.2, 7.2, 7.2, 7. , 5. , 3. , 
     2.5, 2.5, 2. ]) 

Ad ogni elemento della matrice, questa funzione restituisce il valore minimo visto finora.

Se si desidera che un array sia monotonico in aumento, è possibile utilizzare np.maximum.accumulate.

Molte altre funzioni universali in NumPy hanno un metodo accumulate per simulare il looping di un array, applicare la funzione a ciascun elemento e raccogliere i valori restituiti in un array della stessa dimensione.

+1

Wow. Non sapevo di "accumulare"! Questo è molto vicino a Python2 'reduce' (o Python3' functools.reduce'), sono corretto? –

+1

È molto simile - 'accumulate' memorizza il risultato dell'operazione su ciascun elemento restituendo un array della stessa lunghezza, mentre il unfunc's [' reduce'] (http://docs.scipy.org/doc/numpy/reference/ generato/numpy.ufunc.reduce.html) mostra solo il risultato finale (comprimendo l'array). –

+0

Oh sì, hai ragione.Quindi 'reduce' restituirebbe fondamentalmente l'ultimo elemento di ciò che restituisce 'accumula'. –

Problemi correlati