2012-07-24 12 views
6

Quiero transmitir una matriz b a la forma que tomaría si estuviera en una operación aritmética con otra matriz a.¿Hay una mejor manera de transmitir matrices?

Por ejemplo, si a.shape = (3,3) y b fuera un escalar, quiero obtener una matriz cuya forma es (3,3) y se completa con el escalar.

Una forma de hacerlo es la siguiente:

>>> 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]]) 

Aunque esto funciona prácticamente, no puedo evitar sentir que parece un poco raro, y no sería obvio para alguien más mirando el codifica lo que estaba tratando de hacer.

¿Hay alguna forma más elegante de hacer esto? Miré la documentación para np.broadcast, pero es de órdenes de magnitud más lenta.

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 

Respuesta

7

Si lo que desea es llenar una matriz con un escalar, fill es probablemente la mejor opción. Pero parece que quieres algo más generalizado. En lugar de utilizar broadcast, puede usar broadcast_arrays para obtener el resultado que (creo) desea.

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

Esto se generaliza a cualquier dos formas broadcastable:

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

No es tan rápido como su método basado en ufunc, pero sigue siendo el mismo orden de magnitud:

>>> %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 

Pero escalares, fill sigue siendo el favorito claro:

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

Por último, las pruebas adicionales muestran que el enfoque más rápido - por lo menos en algunos casos - es multiplicar por 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 para 'broadcast_arrays()'. – EOL

+0

¡Perfecto! Esto es exactamente lo que estaba buscando. Me sorprende que no lo haya visto; está justo al lado de 'broadcast' en los documentos. – user545424

+0

En caso de que tenga curiosidad, la razón por la que estoy interesado en esto es porque la función 'scipy.ndimage.map_coordinates' no transmite automáticamente las coordenadas de entrada, así que tengo que hacerlo manualmente. – user545424

1

Si sólo tiene que transmitir un escalar en cierta forma arbitraria, se puede hacer algo como esto:

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

Editar: np.tile es más general. Se puede utilizar para duplicar cualquier escalar/vector en cualquier número de dimensiones:

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

'b * np.ones()' implica * multiplicaciones * mientras que solo necesitamos * copiar * valores. 'fill()' es más apropiado y más rápido. – EOL

2

fill suena como la forma más sencilla:

>>> 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: Como @EOL señala, usted don' Necesito arange si desea crear una nueva matriz, np.empty((100,100)) (o la forma que sea) es mejor para esto.

Tiempos:

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

¿Por qué el voto a favor? – Bruno

+0

No hay ninguna razón para usar 'arange()': esto desperdicia tiempo para nada, ya que una matriz debe crearse y llenarse con números que solo se borrarán. – EOL

+1

@EOL, solo estaba tomando el ejemplo en la pregunta para crear una matriz. Es irrelevante para esta pregunta (estaba asumiendo que el arreglo ya estaba allí, esperando ser llenado). – Bruno

1

La solución más rápida y más limpia que sé es:

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