Entrelazando dos matrices numpy

84

Suponga que se dan las siguientes matrices:

a = array([1,3,5])
b = array([2,4,6])

¿Cómo se entrelazarían de manera eficiente para obtener una tercera matriz como esta?

c = array([1,2,3,4,5,6])

Se puede suponer que length(a)==length(b).

DR
fuente
1
¿Qué tal la misma pregunta, pero está intentando entrelazar matrices? Es decir, ayb son tridimensionales y no necesariamente del mismo tamaño en la primera dimensión. Nota: Solo se debe intercalar la primera dimensión.
Geronimo

Respuestas:

144

Me gusta la respuesta de Josh. Solo quería agregar una solución más mundana, habitual y un poco más detallada. No sé cuál es más eficiente. Espero que tengan un rendimiento similar.

import numpy as np
a = np.array([1,3,5])
b = np.array([2,4,6])

c = np.empty((a.size + b.size,), dtype=a.dtype)
c[0::2] = a
c[1::2] = b
Paul
fuente
1
A menos que la velocidad sea realmente importante, me inclinaría por esto, ya que es mucho más comprensible, lo cual es importante si alguien va a volver a mirarlo.
John Salvatier
6
+1 Jugué con los tiempos y su código sorprendentemente parece ser 2-5 veces más rápido dependiendo de las entradas. Sigo considerando que la eficiencia de este tipo de operaciones no es intuitiva, por lo que siempre vale la pena usarla timeitpara probar si una operación en particular es un cuello de botella en su código. Por lo general, hay más de una forma de hacer las cosas en numpy, por lo que definitivamente hay fragmentos de código de perfil.
JoshAdel
@JoshAdel: Supongo que si .reshapecrea una copia adicional de la matriz, eso explicaría un impacto de rendimiento 2x. Sin embargo, no creo que siempre tenga una copia. Supongo que la diferencia de 5x es solo para arreglos pequeños.
Paul
mirando .flagsy probando .basemi solución, parece que la remodelación al formato 'F' crea una copia oculta de los datos vstacked, por lo que no es una vista simple como pensé que sería. Y extrañamente, el 5x es solo para matrices de tamaño intermedio por alguna razón.
JoshAdel
Otra ventaja de esta respuesta es que no se limita a matrices de la misma longitud. Podría tejer nartículos con n-1artículos.
EliadL
62

Pensé que valdría la pena comprobar el rendimiento de las soluciones. Y este es el resultado:

ingrese la descripción de la imagen aquí

Esto muestra claramente que la respuesta más votada y aceptada (la respuesta de Paul) es también la opción más rápida.

El código se tomó de las otras respuestas y de otra sesión de preguntas y respuestas :

# Setup
import numpy as np

def Paul(a, b):
    c = np.empty((a.size + b.size,), dtype=a.dtype)
    c[0::2] = a
    c[1::2] = b
    return c

def JoshAdel(a, b):
    return np.vstack((a,b)).reshape((-1,),order='F')

def xioxox(a, b):
    return np.ravel(np.column_stack((a,b)))

def Benjamin(a, b):
    return np.vstack((a,b)).ravel([-1])

def andersonvom(a, b):
    return np.hstack( zip(a,b) )

def bhanukiran(a, b):
    return np.dstack((a,b)).flatten()

def Tai(a, b):
    return np.insert(b, obj=range(a.shape[0]), values=a)

def Will(a, b):
    return np.ravel((a,b), order='F')

# Timing setup
timings = {Paul: [], JoshAdel: [], xioxox: [], Benjamin: [], andersonvom: [], bhanukiran: [], Tai: [], Will: []}
sizes = [2**i for i in range(1, 20, 2)]

# Timing
for size in sizes:
    func_input1 = np.random.random(size=size)
    func_input2 = np.random.random(size=size)
    for func in timings:
        res = %timeit -o func(func_input1, func_input2)
        timings[func].append(res)

%matplotlib notebook

import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure(1)
ax = plt.subplot(111)

for func in timings:
    ax.plot(sizes, 
            [time.best for time in timings[func]], 
            label=func.__name__)  # you could also use "func.__name__" here instead
ax.set_xscale('log')
ax.set_yscale('log')
ax.set_xlabel('size')
ax.set_ylabel('time [seconds]')
ax.grid(which='both')
ax.legend()
plt.tight_layout()

En caso de que tenga numba disponible, también puede usarlo para crear una función:

import numba as nb

@nb.njit
def numba_interweave(arr1, arr2):
    res = np.empty(arr1.size + arr2.size, dtype=arr1.dtype)
    for idx, (item1, item2) in enumerate(zip(arr1, arr2)):
        res[idx*2] = item1
        res[idx*2+1] = item2
    return res

Podría ser un poco más rápido que las otras alternativas:

ingrese la descripción de la imagen aquí

MSeifert
fuente
2
También es de destacar que la respuesta aceptada es mucho más rápida que la solución nativa de Python con roundrobin()las recetas de itertools.
Brad Solomon
41

Aquí hay un resumen:

c = numpy.vstack((a,b)).reshape((-1,),order='F')
JoshAdel
fuente
16
Vaya, esto es tan ilegible :) Este es uno de los casos en los que si no escribe un comentario adecuado en el código, puede volver loco a alguien.
Ilya Kogan
9
Son solo dos comandos numpy comunes encadenados. No creo que sea tan ilegible, aunque un comentario nunca está de más.
JoshAdel
1
@JohnAdel, bueno, no lo es numpy.vstack((a,b)).interweave():)
Ilya Kogan
6
@Ilya: Hubiera llamado a la función .interleave()personalmente :)
JoshAdel
¿Qué hace reshape?
Danijel
23

Aquí hay una respuesta más simple que algunas de las anteriores.

import numpy as np
a = np.array([1,3,5])
b = np.array([2,4,6])
inter = np.ravel(np.column_stack((a,b)))

Después de esto intercontiene:

array([1, 2, 3, 4, 5, 6])

Esta respuesta también parece ser ligeramente más rápida:

In [4]: %timeit np.ravel(np.column_stack((a,b)))
100000 loops, best of 3: 6.31 µs per loop

In [8]: %timeit np.ravel(np.dstack((a,b)))
100000 loops, best of 3: 7.14 µs per loop

In [11]: %timeit np.vstack((a,b)).ravel([-1])
100000 loops, best of 3: 7.08 µs per loop
xioxox
fuente
10

Esto intercalará / entrelazará las dos matrices y creo que es bastante legible:

a = np.array([1,3,5])      #=> array([1, 3, 5])
b = np.array([2,4,6])      #=> array([2, 4, 6])
c = np.hstack( zip(a,b) )  #=> array([1, 2, 3, 4, 5, 6])
Andersonvom
fuente
2
Me gusta este como el más legible. a pesar de que es la solución más lenta.
kimstik
Envuelva zipen una listadvertencia para evitar la depreciación
Milo Wielondek
6

Tal vez esto sea más legible que la solución de @ JoshAdel:

c = numpy.vstack((a,b)).ravel([-1])
Benjamín
fuente
2
ravel's orderargumento a la documentación es uno de C, F, A, o K. Creo que realmente lo desea .ravel('F'), para el pedido de FORTRAN (primera columna)
Nick T
5

Mejorando la respuesta de @ xioxox:

import numpy as np
a = np.array([1,3,5])
b = np.array([2,4,6])
inter = np.ravel((a,b), order='F')
Será
fuente
1

vstack seguro es una opción, pero una solución más sencilla para su caso podría ser la hstack

>>> a = array([1,3,5])
>>> b = array([2,4,6])
>>> hstack((a,b)) #remember it is a tuple of arrays that this function swallows in.
>>> array([1, 3, 5, 2, 4, 6])
>>> sort(hstack((a,b)))
>>> array([1, 2, 3, 4, 5, 6])

y lo que es más importante, esto funciona para formas arbitrarias de ayb

También es posible que desee probar dstack

>>> a = array([1,3,5])
>>> b = array([2,4,6])
>>> dstack((a,b)).flatten()
>>> array([1, 2, 3, 4, 5, 6])

¡Tienes opciones ahora!

bhanukiran
fuente
7
-1 a la primera respuesta porque la pregunta no tiene nada que ver con la clasificación. +1 a la segunda respuesta, que es la mejor que he visto hasta ahora. Es por eso que las soluciones múltiples deben publicarse como respuestas múltiples. Divídalo en varias respuestas.
endolito
1

Otro one-liner: np.vstack((a,b)).T.ravel()
Uno más:np.stack((a,b),1).ravel()

Pretensioso
fuente
0

También se puede intentar np.insert. (Solución migrada de matrices numpy de Interleave )

import numpy as np
a = np.array([1,3,5])
b = np.array([2,4,6])
np.insert(b, obj=range(a.shape[0]), values=a)

Consulte el documentationy tutorialpara obtener más información.

Tai
fuente
0

Necesitaba hacer esto pero con matrices multidimensionales a lo largo de cualquier eje. Aquí hay una función rápida de propósito general a tal efecto. Tiene la misma firma de llamada que np.concatenate, excepto que todas las matrices de entrada deben tener exactamente la misma forma.

import numpy as np

def interleave(arrays, axis=0, out=None):
    shape = list(np.asanyarray(arrays[0]).shape)
    if axis < 0:
        axis += len(shape)
    assert 0 <= axis < len(shape), "'axis' is out of bounds"
    if out is not None:
        out = out.reshape(shape[:axis+1] + [len(arrays)] + shape[axis+1:])
    shape[axis] = -1
    return np.stack(arrays, axis=axis+1, out=out).reshape(shape)
Clwainwright
fuente