¡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_topara generar simplemente una 3Dvista en la 2Dmatriz 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 stacken 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 arrlargo del primer eje, la solución np.broadcast_topara crear la 3Dvista 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 arrlargo del tercer eje, la solución para crear una 3Dvista 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-dimentrada 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 2Dcaso 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 Nde 3a 3000no 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).