Intrecciando due array numpy


84

Supponiamo che vengano forniti i seguenti array:

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

Come si potrebbero intrecciarli in modo efficiente in modo da ottenere un terzo array come questo

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

Si può presumere che length(a)==length(b).


1
Che ne dici, stessa domanda, ma stai cercando di intercalare le matrici. Cioè aeb sono tridimensionali e non necessariamente della stessa dimensione nella prima dimensione. Nota: solo la prima dimensione deve essere intercalata.
Geronimo

Risposte:


145

Mi piace la risposta di Josh. Volevo solo aggiungere una soluzione più banale, usuale e leggermente più prolissa. Non so quale sia più efficiente. Mi aspetto che avranno prestazioni simili.

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 meno che la velocità non sia davvero molto importante, lo seguirò perché è molto più comprensibile, il che è importante se qualcuno la guarderà di nuovo.
John Salvatier

6
+1 Ho giocato con i tempi e il tuo codice sembra sorprendentemente essere 2-5 volte più veloce a seconda degli input. Trovo ancora che l'efficienza di questi tipi di operazioni non sia intuitiva, quindi vale sempre la pena utilizzarla timeitper testare le cose se una particolare operazione è un collo di bottiglia nel codice. Di solito ci sono più modi per fare le cose in numpy, quindi sicuramente profilare frammenti di codice.
JoshAdel

@ JoshAdel: immagino che se .reshapecrei una copia aggiuntiva dell'array, ciò spiegherebbe un doppio successo nelle prestazioni. Tuttavia, non credo che faccia sempre una copia. Immagino che la differenza 5x sia solo per piccoli array?
Paul

guardando .flagse testando la .basemia soluzione, sembra che il rimodellamento in formato "F" crei una copia nascosta dei dati vstacked, quindi non è una visualizzazione semplice come pensavo sarebbe. E stranamente il 5x è solo per array di dimensioni intermedie per qualche motivo.
JoshAdel

Un altro vantaggio di questa risposta è che non è limitato agli array della stessa lunghezza. Potrebbe intrecciare noggetti con n-1oggetti.
EliadL

63

Ho pensato che potesse valere la pena verificare come le soluzioni si sono comportate in termini di prestazioni. E questo è il risultato:

inserisci qui la descrizione dell'immagine

Questo mostra chiaramente che la risposta più votata e accettata (risposta di Paul) è anche l'opzione più veloce.

Il codice è stato preso dalle altre risposte e da un'altra domanda e risposta :

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

Nel caso in cui tu abbia numba disponibile, potresti anche usarlo per creare una funzione:

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

Potrebbe essere leggermente più veloce delle altre alternative:

inserisci qui la descrizione dell'immagine


2
Inoltre, la risposta accettata è molto più veloce della soluzione Python nativa con roundrobin()le ricette itertools.
Brad Solomon

42

Ecco una battuta:

c = numpy.vstack((a,b)).reshape((-1,),order='F')

17
Wow, è così illeggibile :) Questo è uno dei casi in cui se non scrivi un commento corretto nel codice, può far impazzire qualcuno.
Ilya Kogan

10
Sono solo due comuni comandi numpy messi insieme. Non penserei che sia illeggibile, anche se un commento non fa mai male.
JoshAdel

1
@JohnAdel, beh, non lo è numpy.vstack((a,b)).interweave():)
Ilya Kogan

6
@Ilya: avrei chiamato la funzione .interleave()personalmente :)
JoshAdel

Cosa fa reshape?
Danijel

23

Ecco una risposta più semplice rispetto ad alcune delle precedenti

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

Dopo questo intercontiene:

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

Anche questa risposta sembra essere leggermente più veloce:

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

Questo interleave / interlaccia i due array e credo che sia abbastanza leggibile:

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
Mi piace questo come il più leggibile. nonostante sia la soluzione più lenta.
kimstik

Avvolgere zipin un listavviso per evitare il deprezzamento
Milo Wielondek

6

Forse questo è più leggibile della soluzione di @ JoshAdel:

c = numpy.vstack((a,b)).ravel([-1])

2
ravel's orderargomento nella documentazione è uno dei C, F, A, o K. Penso che tu voglia davvero .ravel('F'), per l'ordine FORTRAN (prima colonna)
Nick T

5

Migliorare la risposta di @ 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 certo è un'opzione, ma una soluzione più semplice per il tuo caso potrebbe essere 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])

e, cosa più importante, funziona per forme arbitrarie di aeb

Inoltre potresti voler provare dstack

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

hai delle opzioni adesso!


7
-1 alla prima risposta perché la domanda non ha nulla a che fare con l'ordinamento. +1 alla seconda risposta, che è la migliore che ho visto finora. Questo è il motivo per cui più soluzioni dovrebbero essere pubblicate come risposte multiple. Si prega di suddividerlo in più risposte.
endolith

1

Avevo bisogno di farlo ma con array multidimensionali lungo qualsiasi asse. Ecco una rapida funzione generale in tal senso. Ha la stessa firma di chiamata di np.concatenate, tranne per il fatto che tutti gli array di input devono avere esattamente la stessa 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)

+1 per una ricetta così generalizzata (funziona con n-dim, interlaccia lungo qualsiasi asse, funziona per qualsiasi numero di array di input, accetta un outarg opzionale e funziona per array sottoclasse). Personalmente, preferirei axisl'impostazione predefinita su -1piuttosto che su 0, ma forse sono solo io. E potresti voler collegare a questa tua risposta, da questa domanda , che in realtà richiedeva che gli array di input fossero n-dimensionali.
Fountainhead

1

Un'altra battuta: np.vstack((a,b)).T.ravel()
un'altra:np.stack((a,b),1).ravel()


0

Si può anche provare np.insert. (Soluzione migrata da array numpy 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)

Si prega di consultare il documentatione tutorialper ulteriori informazioni.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.