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).


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

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í


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')

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

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])

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])

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')

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!


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()



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)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.