2012-07-24 25 views
6

Voglio trasmettere un array b alla forma che sarebbe necessario se fosse in un'operazione aritmetica con un altro array a.C'è un modo migliore per trasmettere gli array?

Ad esempio, se a.shape = (3,3) e era uno scalare, voglio ottenere un array la cui forma è (3,3) e viene riempito con lo scalare.

Un modo per farlo è come questo:

>>> import numpy as np 
>>> a = np.arange(9).reshape((3,3)) 
>>> b = 1 + a*0 
>>> b 
array([[1, 1, 1], 
     [1, 1, 1], 
     [1, 1, 1]]) 

Anche se questo funziona in pratica, non posso fare a meno di sentire che sembra un po 'strano, e non sarebbe evidente a qualcun altro a guardare il codice che stavo cercando di fare.

Esiste un modo più elegante per farlo? Ho esaminato la documentazione di np.broadcast, ma sono gli ordini di grandezza più lenti.

In [1]: a = np.arange(10000).reshape((100,100)) 

In [2]: %timeit 1 + a*0 
10000 loops, best of 3: 31.9 us per loop 

In [3]: %timeit bc = np.broadcast(a,1);np.fromiter((v for u, v in bc),float).reshape(bc.shape) 
100 loops, best of 3: 5.2 ms per loop 

In [4]: 5.2e-3/32e-6 
Out[4]: 162.5 

risposta

7

Se si desidera solo per riempire una matrice con uno scalare, fill è probabilmente la scelta migliore. Ma sembra che tu voglia qualcosa di più generalizzato. Piuttosto che usare broadcast puoi usare broadcast_arrays per ottenere il risultato che (penso) tu desideri.

>>> a = numpy.arange(9).reshape(3, 3) 
>>> numpy.broadcast_arrays(a, 1)[1] 
array([[1, 1, 1], 
     [1, 1, 1], 
     [1, 1, 1]]) 

Questo generalizza a qualsiasi due forme broadcastable:

>>> numpy.broadcast_arrays(a, [1, 2, 3])[1] 
array([[1, 2, 3], 
     [1, 2, 3], 
     [1, 2, 3]]) 

Non è abbastanza veloce come il tuo ufunc metodo-based, ma è ancora sullo stesso ordine di grandezza:

>>> %timeit 1 + a * 0 
10000 loops, best of 3: 23.2 us per loop 
>>> %timeit numpy.broadcast_arrays(a, 1)[1] 
10000 loops, best of 3: 52.3 us per loop 

Ma scalari, fill è ancora il chiaro front-runner:

>>> %timeit b = numpy.empty_like(a, dtype='i8'); b.fill(1) 
100000 loops, best of 3: 6.59 us per loop 

Infine, ulteriori test dimostra che l'approccio più veloce - almeno in alcuni casi - è quello di moltiplicare per ones:

>>> %timeit numpy.broadcast_arrays(a, numpy.arange(100))[1] 
10000 loops, best of 3: 53.4 us per loop 
>>> %timeit (1 + a * 0) * numpy.arange(100) 
10000 loops, best of 3: 45.9 us per loop 
>>> %timeit b = numpy.ones_like(a, dtype='i8'); b * numpy.arange(100) 
10000 loops, best of 3: 28.9 us per loop 
+0

+1 per 'broadcast_arrays()'. – EOL

+0

Perfetto! Questo e 'esattamente quello che stavo cercando. Sono sorpreso di non averlo visto; è proprio accanto a 'broadcast' nei documenti. – user545424

+0

Nel caso in cui sei curioso, la ragione per cui sono interessato a questo è che la funzione 'scipy.ndimage.map_coordinates' non trasmette automaticamente le coordinate di input, quindi devo farlo manualmente. – user545424

1

Se avete solo bisogno di trasmettere uno scalare a qualche forma arbitraria, si può fare qualcosa di simile:

a = b*np.ones(shape=(3,3)) 

Edit: np.tile è più generale. Si può usare per duplicare qualsiasi scalare/vettore in qualsiasi numero di dimensioni:

b = 1 
N = 100 
a = np.tile(b, reps=(N, N)) 
+1

'b * np.ones()' implica * moltiplicazioni * mentre abbiamo solo bisogno di * copiare * valori. 'fill()' è sia più appropriato che più veloce. – EOL

2

fill suona come il modo più semplice:

>>> a = np.arange(9).reshape((3,3)) 
>>> a 
array([[0, 1, 2], 
     [3, 4, 5], 
     [6, 7, 8]]) 
>>> a.fill(10) 
>>> a 
array([[10, 10, 10], 
     [10, 10, 10], 
     [10, 10, 10]]) 

EDIT: Come @EOL fa notare, si don' t necessario arange se si desidera creare un nuovo array, np.empty((100,100)) (o qualsiasi altra forma) è meglio per questo.

Timings:

In [3]: a = np.arange(10000).reshape((100,100)) 
In [4]: %timeit 1 + a*0 
100000 loops, best of 3: 19.9 us per loop 

In [5]: a = np.arange(10000).reshape((100,100)) 
In [6]: %timeit a.fill(1) 
100000 loops, best of 3: 3.73 us per loop 
+0

Perché il downvote? – Bruno

+0

Non c'è alcun motivo per usare 'arange()': questo spreca tempo per niente, poiché una matrice deve essere creata e riempita di numeri che verranno cancellati. – EOL

+1

@EOL, stavo solo prendendo l'esempio nella domanda per creare un array. È irrilevante per questa domanda (stavo dando per scontato che l'array fosse già lì, in attesa di essere riempito.) – Bruno

1

La soluzione più veloce e più pulito che so è:

b_arr = numpy.empty(a.shape) # Empty array 
b_arr.fill(b) # Filling with one value