2015-04-13 11 views
10

Desidero aggiungere array numpy con datatyp uint8. So che i valori di questi array possono essere abbastanza grandi da consentire un overflow. In modo da ottenere qualcosa di simile:Evitare overflow durante l'aggiunta di array numpy

a = np.array([100, 200, 250], dtype=np.uint8) 
b = np.array([50, 50, 50], dtype=np.uint8) 
a += b 

Ora, a è [150 250 44]. Tuttavia, al posto di un overflow, voglio che i valori troppo grandi per uint8 siano i massimi consentiti per uint8. Quindi il mio risultato desiderato sarebbe [150 250 255].

ho potuto ottenere questo risultato con il seguente codice:

a = np.array([100, 200, 250], dtype=np.uint8) 
b = np.array([50, 50, 50], dtype=np.uint8) 
c = np.zeros((1,3), dtype=np.uint16) 
c += a 
c += b 
c[c>255] = 255 
a = np.array(c, dtype=np.uint8) 

Il problema è, che le mie matrici sono veramente grandi creando così un terzo array con un tipo di dati più grande potrebbe essere un problema di memoria. Esiste un modo rapido e più efficiente di memoria per ottenere il risultato descritto?

risposta

6

È possibile raggiungere questo obiettivo la creazione di una terza serie di DTYPE uint8, oltre a una serie bool (che insieme sono più efficienti in termini di memoria di un array uint16).

np.putmask è utile per evitare un array temporaneo.

a = np.array([100, 200, 250], dtype=np.uint8) 
b = np.array([50, 50, 50], dtype=np.uint8) 
c = 255 - b # a temp uint8 array here 
np.putmask(a, c < a, c) # a temp bool array here 
a += b 

Tuttavia, come @moarningsun sottolinea correttamente, una matrice bool batte la stessa quantità di memoria, come una matrice uint8, quindi questo non è necessariamente utile. È possibile risolvere questo evitando avere più di una temperatura matrice in un dato momento:

a = np.array([100, 200, 250], dtype=np.uint8) 
b = np.array([50, 50, 50], dtype=np.uint8) 
b = 255 - b # old b is gone shortly after new array is created 
np.putmask(a, b < a, b) # a temp bool array here, then it's gone 
a += 255 - b # a temp array here, then it's gone 

Questo approccio scambia consumo di memoria per la CPU.


Un altro approccio è quello di precalculate tutti i risultati possibili, che è O (1) memoria aggiuntiva (cioè indipendente dalla dimensione degli array):

c = np.clip(np.arange(256) + np.arange(256)[..., np.newaxis], 0, 255).astype(np.uint8) 
c 
=> array([[ 0, 1, 2, ..., 253, 254, 255], 
      [ 1, 2, 3, ..., 254, 255, 255], 
      [ 2, 3, 4, ..., 255, 255, 255], 
      ..., 
      [253, 254, 255, ..., 255, 255, 255], 
      [254, 255, 255, ..., 255, 255, 255], 
      [255, 255, 255, ..., 255, 255, 255]], dtype=uint8) 

c[a,b] 
=> array([150, 250, 255], dtype=uint8) 

Questo approccio è la maggior parte della memoria -efficiente se i tuoi array sono molto grandi. Anche in questo caso, è costoso nel tempo di elaborazione, perché sostituisce l'aggiunta super veloce con l'indicizzazione a 2dim-array più lenta.

SPIEGAZIONE DI COME FUNZIONA

costruzione della matrice c sopra fa uso di un trucco di trasmissione NumPy. L'aggiunta di una matrice di forma (N,) e la matrice di forma (1,N) trasmettono entrambi come (N,N) -like, quindi il risultato è una matrice NxN di tutte le possibili somme. Quindi, lo tagliamo. Otteniamo un array 2dim che soddisfa: c[i,j]=min(i+j,255) per ogni i, j.

Quindi ciò che rimane è usare la fantasia indicizzazione per afferrare i giusti valori.Lavorare con l'ingresso che hai fornito, accediamo:

c[([100, 200, 250] , [50, 50, 50])] 

Il primo indice-array si riferisce alla prima debole, e la seconda per il 2 ° dim. Pertanto, il risultato è un array della stessa forma degli array di indici ((N,)), costituito dai valori [ c[100,50] , c[200,50] , c[250,50] ].

+1

Non conoscevo 'putmask', grazie per quello! Usando questa funzione, penso che 'a + = b' seguito da' np.putmask (a, a

+0

@moarningsun Penso che tu abbia ragione. Tuttavia si basa sullo straripamento, che personalmente non mi sento perfettamente a mio agio con ... – shx2

+0

@ moarningsun perché hai cancellato la tua risposta? Penso che sia una risposta decente e funziona – shx2

0

ne dite di fare

>>> a + np.minimum(255 - a, b) 
array([150, 250, 255], dtype=uint8) 

in generale ottenere il valore massimo per il vostro tipo di dati con

np.iinfo(np.uint8).max 
+0

@PadraicCunningham, lo fa, ma di DTYPE uint8, non uint16. Tuttavia, crea * tre * temp uint8 matrici .. – shx2

+0

@ shx2: Ne conto solo due. '255 - a', e' np.minimum (255 - a, b) '. Qual è il terzo? – user2357112

+0

@ user2357112, il 'a + ...'. Se OP vuole il risultato al posto della matrice 'a', è possibile evitarlo. – shx2

1

Ecco un modo:

>>> a = np.array([100, 200, 250], dtype=np.uint8) 
>>> b = np.array([50, 50, 50], dtype=np.uint8) 
>>> a+=b; a[a<b]=255 
>>> a 
array([150, 250, 255], dtype=uint8) 
1

si può fare veramente inplace con Numba, ad esempio:

import numba 

@numba.jit('void(u1[:],u1[:])', locals={'temp': numba.uint16}) 
def add_uint8_inplace_clip(a, b): 
    for i in range(a.shape[0]): 
     temp = a[i] + b[i] 
     a[i] = temp if temp<256 else 255 

add_uint8_inplace_clip(a, b) 

O con Numexpr, ad esempio:

import numexpr 

numexpr.evaluate('where((a+b)>255, 255, a+b)', out=a, casting='unsafe') 

Numexpr upcasts a int32 internamente, prima di rimetterlo nelloArray.

0

C'è a function in numpy per questo:

numpy.nan_to_num(x)[source]

Sostituire nan con zero e inf con numeri finiti.

Restituisce un array o scalare sostituendo Not a Number (NaN) con zero, infinito (positivo) con un numero molto grande e infinito negativo con un numero molto piccolo (o negativo).

Nuovo array con la stessa forma di x e dtype dell'elemento in x con la massima precisione.

Se x non è esatto, NaN viene sostituito da zero e l'infinito (-infinità) viene sostituito dal valore in virgola mobile più grande (più piccolo o più negativo) che si adatta al dtype di uscita. Se x non è inesatto, viene restituita una copia di x.

non sono sicuro se funzionerà con uint8, a causa della menzione di virgola mobile in uscita, ma per gli altri lettori, può essere utile

+1

Non vedo come questo possa essere d'aiuto per la domanda. Non ci sono né NaN né valori infiniti in nessuno degli array che devono essere aggiunti. Quindi forse mi manca il punto della tua risposta? – Thomas

+0

@Thomas hmm, forse è diverso per i tipi interi, ma quando ho incontrato il problema con float, gli overflow sono apparso come +/- infiniti –

+0

@ToshinouKyouko Sì, è davvero diverso per i numeri interi, semplicemente overflow come nell'esempio di OPERAZIONE. – luator