np.concatenate un tensor / matriz ND con una matriz 1D

Tengo dos matrices a y b

a.shape (5, 4, 3) array([[[ 0. , 0. , 0. ], [ 0. , 0. , 0. ], [ 0. , 0. , 0. ], [ 0.10772717, 0.604584 , 0.41664413]], [[ 0. , 0. , 0. ], [ 0. , 0. , 0. ], [ 0.10772717, 0.604584 , 0.41664413], [ 0.95879616, 0.85575133, 0.46135877]], [[ 0. , 0. , 0. ], [ 0.10772717, 0.604584 , 0.41664413], [ 0.95879616, 0.85575133, 0.46135877], [ 0.70442301, 0.74126523, 0.88965603]], [[ 0.10772717, 0.604584 , 0.41664413], [ 0.95879616, 0.85575133, 0.46135877], [ 0.70442301, 0.74126523, 0.88965603], [ 0.8039435 , 0.62802183, 0.58885027]], [[ 0.95879616, 0.85575133, 0.46135877], [ 0.70442301, 0.74126523, 0.88965603], [ 0.8039435 , 0.62802183, 0.58885027], [ 0.95848603, 0.72429311, 0.71461332]]]) 

y B

 array([ 0.79212707, 0.66629398, 0.58676553], dtype=float32) b.shape (3,) 

Quiero conseguir matriz

 ab.shape (5,5,3) 

Hago lo siguiente abajo primero

 b = b.reshape(1,1,3) 

entonces

 b=np.concatenate((b, b,b, b, b), axis = 0) 

Y

 ab=np.concatenate((a, b), axis = 1) ab.shape (5, 5, 3) 

Obtengo el resultado correcto, pero no es muy conveniente, especialmente en el paso.

 b=np.concatenate((b, b,b, b, b), axis = 0) 

cuando tengo que escribir muchas veces (el conjunto de datos real tiene muchas dimensiones). ¿Hay alguna forma más rápida de llegar a este resultado?

Puedes usar np.repeat

 r = np.concatenate((a, b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)), axis=1) 

Lo que esto hace es cambiar primero su matriz b para que coincida con las dimensiones de a , y luego repetir sus valores tantas veces como sea necesario de acuerdo con el primer eje de a:

 b3D = b.reshape(1, 1, -1).repeat(a.shape[0], axis=0) array([[[1, 2, 3]], [[1, 2, 3]], [[1, 2, 3]], [[1, 2, 3]], [[1, 2, 3]]]) b3D.shape (5, 1, 3) 

Este resultado intermedio se concatena con a

 r = np.concatenate((a, b3d), axis=0) r.shape (5, 5, 3) 

Esto difiere de su respuesta actual principalmente en el hecho de que la repetición de valores no está codificada (es decir, la repetición la cuida).

Si necesita manejar esto para un número diferente de dimensiones (no arrays 3D), entonces se necesitan algunos cambios (principalmente en la forma en que se elimina la remodelación codificada de b ).


Tiempos

 a = np.random.randn(100, 99, 100) b = np.random.randn(100) 

 # Tai's answer %timeit np.insert(a, 4, b, axis=1) 100 loops, best of 3: 3.7 ms per loop # Divakar's answer %%timeit b3D = np.broadcast_to(b,(a.shape[0],1,len(b))) np.concatenate((a,b3D),axis=1) 100 loops, best of 3: 3.67 ms per loop # solution in this post %timeit np.concatenate((a, b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)), axis=1) 100 loops, best of 3: 3.62 ms per loop 

Estas son todas soluciones bastante competitivas. Sin embargo, tenga en cuenta que el rendimiento depende de sus datos reales, así que asegúrese de probar primero las cosas.

Simplemente transmita b a 3D y luego concatene a lo largo del segundo eje –

 b3D = np.broadcast_to(b,(a.shape[0],1,len(b))) out = np.concatenate((a,b3D),axis=1) 

La parte de broadcasting con np.broadcast_to no se replica o hace copias reales y es simplemente una vista replicada y luego, en el siguiente paso, hacemos la concatenación que hace la replicación sobre la marcha.

Benchmarking

Estamos comparando la versión np.repeat de la solución de @cᴏʟᴅsᴘᴇᴇᴅ con np.broadcast_to uno en esta sección con enfoque en el rendimiento. La difusión basada en uno hace la replicación y la concatenación en el segundo paso, como un comando combinado por así decirlo, mientras que la versión np.repeat hace copia y luego concatena en dos pasos separados.

Progtwigción de los enfoques en su conjunto:

Caso # 1: a = (500,400,300) b = (300,)

 In [321]: a = np.random.rand(500,400,300) In [322]: b = np.random.rand(300) In [323]: %%timeit ...: b3D = b.reshape(1, 1, -1).repeat(a.shape[0], axis=0) ...: r = np.concatenate((a, b3D), axis=1) 10 loops, best of 3: 72.1 ms per loop In [325]: %%timeit ...: b3D = np.broadcast_to(b,(a.shape[0],1,len(b))) ...: out = np.concatenate((a,b3D),axis=1) 10 loops, best of 3: 72.5 ms per loop 

Para formas de entrada más pequeñas, la llamada a np.broadcast_to tomaría un poco más de tiempo que np.repeat dado que el trabajo necesario para configurar la transmisión es aparentemente más complicado, como lo sugieren los tiempos a continuación:

 In [360]: a = np.random.rand(5,4,3) In [361]: b = np.random.rand(3) In [366]: %timeit np.broadcast_to(b,(a.shape[0],1,len(b))) 100000 loops, best of 3: 3.12 µs per loop In [367]: %timeit b.reshape(1, 1, -1).repeat(a.shape[0], axis=0) 1000000 loops, best of 3: 957 ns per loop 

Pero, la parte de transmisión tendría un tiempo constante independientemente de las formas de las entradas, es decir, la parte de 3 u-sec permanecería alrededor de esa marca. El tiempo para la contraparte: b.reshape(1, 1, -1).repeat(a.shape[0], axis=0) dependerá de las formas de entrada. Entonces, profundicemos y veamos cómo se comportan los pasos de concatenación para los dos enfoques justos.

Cavando más profundo

Intentando profundizar para ver cuánto consume la parte de concatenación:

 In [353]: a = np.random.rand(500,400,300) In [354]: b = np.random.rand(300) In [355]: b3D = np.broadcast_to(b,(a.shape[0],1,len(b))) In [356]: %timeit np.concatenate((a,b3D),axis=1) 10 loops, best of 3: 72 ms per loop In [357]: b3D = b.reshape(1, 1, -1).repeat(a.shape[0], axis=0) In [358]: %timeit np.concatenate((a,b3D),axis=1) 10 loops, best of 3: 72 ms per loop 

Conclusión: no parece muy diferente.

Ahora, probemos un caso donde la replicación necesaria para b es un número mayor y b tiene también un número notablemente alto de elementos.

 In [344]: a = np.random.rand(10000, 10, 1000) In [345]: b = np.random.rand(1000) In [346]: b3D = np.broadcast_to(b,(a.shape[0],1,len(b))) In [347]: %timeit np.concatenate((a,b3D),axis=1) 10 loops, best of 3: 130 ms per loop In [348]: b3D = b.reshape(1, 1, -1).repeat(a.shape[0], axis=0) In [349]: %timeit np.concatenate((a,b3D),axis=1) 10 loops, best of 3: 141 ms per loop 

Conclusión: parece que la concatenación y la replicación np.broadcast_to con np.broadcast_to están np.broadcast_to un poco aquí.

Probemos el caso original de (5,4,3) forma:

 In [360]: a = np.random.rand(5,4,3) In [361]: b = np.random.rand(3) In [362]: b3D = np.broadcast_to(b,(a.shape[0],1,len(b))) In [363]: %timeit np.concatenate((a,b3D),axis=1) 1000000 loops, best of 3: 948 ns per loop In [364]: b3D = b.reshape(1, 1, -1).repeat(a.shape[0], axis=0) In [365]: %timeit np.concatenate((a,b3D),axis=1) 1000000 loops, best of 3: 950 ns per loop 

Conclusión: De nuevo, no muy diferente.

Entonces, la conclusión final es que si hay una gran cantidad de elementos en b y si el primer eje de a también es un número grande (ya que el número de replicación es ese), np.broadcast_to sería una buena opción, de lo contrario np.repeat versión basada en la np.repeat se ocupa de los otros casos bastante bien.

Aquí hay algunos tiempos simples basados ​​en las soluciones de cᴏʟᴅsᴘᴇᴇᴅ y Divakar:

 %timeit np.concatenate((a, b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)), axis=1) 

Salida: La carrera más lenta tomó 6.44 veces más que la más rápida. Esto podría significar que se está almacenando en caché un resultado intermedio. 100000 bucles, lo mejor de 3: 3.68 µs por bucle

 %timeit np.concatenate((a, np.broadcast_to(b[None,None], (a.shape[0], 1, len(b)))), axis=1) 

Salida: La carrera más lenta tomó 4.12 veces más que la más rápida. Esto podría significar que se está almacenando en caché un resultado intermedio. 100000 bucles, lo mejor de 3: 10.7 µs por bucle

Ahora aquí está el tiempo basado en su código original:

 %timeit original_func(a, b) 

Salida: La carrera más lenta tomó 4.62 veces más que la más rápida. Esto podría significar que se está almacenando en caché un resultado intermedio. 100000 bucles, el mejor de 3: 4,69 µs por bucle

Ya que la pregunta formulaba formas más rápidas de obtener el mismo resultado, buscaría la solución de cᴏʟᴅsᴘᴇᴇᴅ basada en estos cálculos de problemas.

También puede utilizar np.insert .

 b_broad = np.expand_dims(b, axis=0) # b_broad.shape = (1, 3) ab = np.insert(a, 4, b_broad, axis=1) """ Because now we are inserting along axis 1 a'shape without axis 1 = (5, 3) b_broad's shape (1, 3) can be aligned and broadcast b_broad to (5, 3) """ 

En este ejemplo, insertamos a lo largo del eje 1, y pondremos b_broad antes del índice dado, 4 aquí. En otras palabras, b_broad ocupará el índice 4 a lo largo del eje y hará que ab.shape igual (5, 5, 3) .

Tenga en cuenta una vez más que antes de que hagamos la inserción, convertimos b en b_broad para lograr de manera segura la transmisión correcta que desea. La dimensión de b es más pequeña y habrá transmisión en el momento de la inserción. Podemos usar expand_dims para lograr este objective.

Si a es de forma (3, 4, 5) , necesitará que b_broad tenga forma (3, 1) para hacer coincidir las dimensiones si se inserta a lo largo del eje 1. Esto se puede lograr mediante

 b_broad = np.expand_dims(b, axis=1) # shape = (3, 1) 

Sería una buena práctica hacer b_broad en la forma correcta, ya que es posible que tenga a.shape = (3, 4, 3) y realmente necesita especificar la forma de transmisión en este caso.

Resultados de tiempo

Del conjunto de datos de OP: la respuesta de COLDSPEED es 3 veces más rápida.

 def Divakar(): # Divakar's answer b3D = b.reshape(1, 1, -1).repeat(a.shape[0], axis=0) r = np.concatenate((a, b3D), axis=1) # COLDSPEED's result %timeit np.concatenate((a, b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)), axis=1) 2.95 µs ± 164 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) # Divakar's result %timeit Divakar() 3.03 µs ± 173 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) # Mine's %timeit np.insert(a, 4, b, axis=1) 10.1 µs ± 220 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) 

Conjunto de datos 2 (Pedir prestado el experimento de tiempo de COLDSPEED): nada se puede concluir en este caso porque comparten casi la misma media y desviación estándar.

 a = np.random.randn(100, 99, 100) b = np.random.randn(100) # COLDSPEED's result %timeit np.concatenate((a, b.reshape(1, 1, -1).repeat(a.shape[0], axis=0)), axis=1) 2.37 ms ± 194 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) # Divakar's %timeit Divakar() 2.31 ms ± 249 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) # Mine's %timeit np.insert(a, 99, b, axis=1) 2.34 ms ± 154 µs per loop (mean ± std. dev. of 7 runs, 100 loops each) 

La velocidad dependerá del tamaño, la forma y el volumen de los datos. Por favor, pruebe en su conjunto de datos si la velocidad es su preocupación.