Probablemente la forma más limpia es usar np.repeat:
a = np.array([[1, 2], [1, 2]])
print(a.shape)
# (2, 2)
# indexing with np.newaxis inserts a new 3rd dimension, which we then repeat the
# array along, (you can achieve the same effect by indexing with None, see below)
b = np.repeat(a[:, :, np.newaxis], 3, axis=2)
print(b.shape)
# (2, 2, 3)
print(b[:, :, 0])
# [[1 2]
# [1 2]]
print(b[:, :, 1])
# [[1 2]
# [1 2]]
print(b[:, :, 2])
# [[1 2]
# [1 2]]
Habiendo dicho eso, a menudo puede evitar repetir sus matrices por completo utilizando la transmisión . Por ejemplo, digamos que quería agregar un (3,)vector:
c = np.array([1, 2, 3])
a a. Podría copiar el contenido de a3 veces en la tercera dimensión, luego copiar el contenido de cdos veces tanto en la primera como en la segunda dimensión, de modo que ambas matrices fueran (2, 2, 3), luego calcular su suma. Sin embargo, es mucho más sencillo y rápido hacer esto:
d = a[..., None] + c[None, None, :]
Aquí, a[..., None]tiene forma (2, 2, 1)y c[None, None, :]tiene forma (1, 1, 3)*. Cuando calculo la suma, el resultado se 'difunde' a lo largo de las dimensiones del tamaño 1, lo que me da un resultado de forma (2, 2, 3):
print(d.shape)
# (2, 2, 3)
print(d[..., 0]) # a + c[0]
# [[2 3]
# [2 3]]
print(d[..., 1]) # a + c[1]
# [[3 4]
# [3 4]]
print(d[..., 2]) # a + c[2]
# [[4 5]
# [4 5]]
La transmisión es una técnica muy poderosa porque evita la sobrecarga adicional involucrada en la creación de copias repetidas de sus matrices de entrada en la memoria.
* Aunque los incluí para mayor claridad, los Noneíndices en cno son realmente necesarios; también puede hacerlo a[..., None] + c, es decir, transmitir una (2, 2, 1)matriz contra una (3,)matriz. Esto se debe a que si una de las matrices tiene menos dimensiones que la otra, solo las dimensiones finales de las dos matrices deben ser compatibles. Para dar un ejemplo más complicado:
a = np.ones((6, 1, 4, 3, 1)) # 6 x 1 x 4 x 3 x 1
b = np.ones((5, 1, 3, 2)) # 5 x 1 x 3 x 2
result = a + b # 6 x 5 x 4 x 3 x 2
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).np.newaxises solo un alias deNoneOtra forma es utilizar
numpy.dstack. Suponiendo que desea repetir losanum_repeatstiempos de la matriz :El truco consiste en envolver la matriz
aen una lista de un solo elemento, luego usar el*operador para duplicar los elementos en esta listanum_repeatsveces.Por ejemplo, si:
Esto repite la matriz de
[1 2; 1 2]5 veces en la tercera dimensión. Para verificar (en IPython):Al final podemos ver que la forma de la matriz es
2 x 2, con 5 cortes en la tercera dimensión.fuente
reshape? ¿Más rápido? da la misma estructura? Definitivamente es más ordenado.¡Utilice una vista y obtenga tiempo de ejecución gratis! Ampliar genérico
n-dimmatrices an+1-dimIntroducido en NumPy
1.10.0, podemos aprovecharnumpy.broadcast_topara generar simplemente una3Dvista en la2Dmatriz 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-dimcasos .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ónnp.broadcast_topara crear la3Dvista sería:Apilar a lo largo del tercer / último eje
Para apilar la entrada a lo
arrlargo del tercer eje, la solución para crear una3Dvista sería:Si realmente necesitamos una copia de memoria, siempre podemos agregarla
.copy(). Por lo tanto, las soluciones serían:Así es como funciona el apilamiento para los dos casos, que se muestra con su información de forma para un caso de muestra:
Las mismas soluciones funcionarían para extender una
n-dimentrada an+1-dimver la salida a lo largo del primer y último eje. Exploremos algunos casos de mayor atenuación:Caso de entrada 3D:
Caso de entrada 4D:
y así.
Tiempos
Usemos un
2Dcaso de muestra grande y obtengamos los tiempos y verifiquemos que la salida sea unview.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) -
Consigamos los tiempos para mostrar que es prácticamente gratis.
Al ser una vista, el aumento
Nde3a3000no cambió nada en los tiempos y ambos son insignificantes en las unidades de tiempo. Por lo tanto, eficiente tanto en memoria como en rendimiento.fuente
Edite @ Mr.F, para preservar el orden de las dimensiones:
fuente
B.shapeimprime(N, 2, 2)para cualquier valor deN. Si transponeBconB.T, coincide con la salida esperada.B[0], B[1],...le dará la rebanada de la derecha, que voy a argumentar y decir que es más fácil de escribir en lugar de utilizarB[:,:,0], B[:,:,1], etc.B[:,:,i]y eso es a lo que estoy acostumbrado.Aquí hay un ejemplo de transmisión que hace exactamente lo que se solicitó.
Entonces
b*aes el resultado deseado y(b*a)[:,:,0]producearray([[1, 2],[1, 2]]), que es el originala, como lo hace(b*a)[:,:,1], etc.fuente
Esto ahora también se puede lograr usando np.tile de la siguiente manera:
fuente