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 a
3 veces en la tercera dimensión, luego copiar el contenido de c
dos 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 c
no 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.newaxis
es solo un alias deNone
Otra forma es utilizar
numpy.dstack
. Suponiendo que desea repetir losa
num_repeats
tiempos de la matriz :El truco consiste en envolver la matriz
a
en una lista de un solo elemento, luego usar el*
operador para duplicar los elementos en esta listanum_repeats
veces.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-dim
matrices an+1-dim
Introducido en NumPy
1.10.0
, podemos aprovecharnumpy.broadcast_to
para generar simplemente una3D
vista en la2D
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ónnp.broadcast_to
para crear la3D
vista sería:Apilar a lo largo del tercer / último eje
Para apilar la entrada a lo
arr
largo del tercer eje, la solución para crear una3D
vista 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-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:
Caso de entrada 4D:
y así.
Tiempos
Usemos un
2D
caso 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
N
de3
a3000
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.fuente
Edite @ Mr.F, para preservar el orden de las dimensiones:
fuente
B.shape
imprime(N, 2, 2)
para cualquier valor deN
. Si transponeB
conB.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*a
es 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