¡Utilice una vista y obtenga tiempo de ejecución gratis! Ampliar genéricon-dim
matrices an+1-dim
Introducido en NumPy1.10.0
, podemos aprovechar numpy.broadcast_to
para generar simplemente una 3D
vista en la 2D
matriz de entrada. El beneficio sería la ausencia de sobrecarga de memoria adicional y el tiempo de ejecución prácticamente gratuito. Esto sería esencial en los casos en que las matrices son grandes y podemos trabajar con vistas. Además, esto funcionaría con genéricon-dim
casos .
Usaría la palabra stack
en lugar decopy
, ya que los lectores podrían confundirla con la copia de matrices que crea copias de memoria.
Apilar a lo largo del primer eje
Si queremos apilar la entrada a lo arr
largo del primer eje, la solución np.broadcast_to
para crear la 3D
vista sería:
np.broadcast_to(arr,(3,)+arr.shape) # N = 3 here
Apilar a lo largo del tercer / último eje
Para apilar la entrada a lo arr
largo del tercer eje, la solución para crear una 3D
vista sería:
np.broadcast_to(arr[...,None],arr.shape+(3,))
Si realmente necesitamos una copia de memoria, siempre podemos agregarla .copy()
. Por lo tanto, las soluciones serían:
np.broadcast_to(arr,(3,)+arr.shape).copy()
np.broadcast_to(arr[...,None],arr.shape+(3,)).copy()
Así es como funciona el apilamiento para los dos casos, que se muestra con su información de forma para un caso de muestra:
# Create a sample input array of shape (4,5)
In [55]: arr = np.random.rand(4,5)
# Stack along first axis
In [56]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[56]: (3, 4, 5)
# Stack along third axis
In [57]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[57]: (4, 5, 3)
Las mismas soluciones funcionarían para extender una n-dim
entrada an+1-dim
ver la salida a lo largo del primer y último eje. Exploremos algunos casos de mayor atenuación:
Caso de entrada 3D:
In [58]: arr = np.random.rand(4,5,6)
# Stack along first axis
In [59]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[59]: (3, 4, 5, 6)
# Stack along last axis
In [60]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[60]: (4, 5, 6, 3)
Caso de entrada 4D:
In [61]: arr = np.random.rand(4,5,6,7)
# Stack along first axis
In [62]: np.broadcast_to(arr,(3,)+arr.shape).shape
Out[62]: (3, 4, 5, 6, 7)
# Stack along last axis
In [63]: np.broadcast_to(arr[...,None],arr.shape+(3,)).shape
Out[63]: (4, 5, 6, 7, 3)
y así.
Tiempos
Usemos un 2D
caso de muestra grande y obtengamos los tiempos y verifiquemos que la salida sea un view
.
# Sample input array
In [19]: arr = np.random.rand(1000,1000)
Demostremos que la solución propuesta es realmente una vista. Usaremos apilamiento a lo largo del primer eje (los resultados serían muy similares para apilar a lo largo del tercer eje) -
In [22]: np.shares_memory(arr, np.broadcast_to(arr,(3,)+arr.shape))
Out[22]: True
Consigamos los tiempos para mostrar que es prácticamente gratis.
In [20]: %timeit np.broadcast_to(arr,(3,)+arr.shape)
100000 loops, best of 3: 3.56 µs per loop
In [21]: %timeit np.broadcast_to(arr,(3000,)+arr.shape)
100000 loops, best of 3: 3.51 µs per loop
Al ser una vista, el aumento N
de 3
a 3000
no cambió nada en los tiempos y ambos son insignificantes en las unidades de tiempo. Por lo tanto, eficiente tanto en memoria como en rendimiento.
b[:,:,0]
,b[:,:,1]
yb[:,:,2]
. Cada segmento de la tercera dimensión es una copia de la matriz 2D original. Esto no es tan obvio con solo mirarloprint(b)
.