Verweben von zwei numpy Arrays

83

Angenommen, die folgenden Arrays sind angegeben:

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

Wie würde man sie effizient verweben, so dass man ein drittes Array wie dieses erhält?

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

Es ist davon auszugehen, dass length(a)==length(b).

DR
quelle
1
Wie wäre es mit derselben Frage, aber Sie versuchen, Matrizen zu verschachteln. Das heißt, a und b sind dreidimensional und in der ersten Dimension nicht unbedingt gleich groß. Hinweis: Nur die erste Dimension sollte verschachtelt werden.
Geronimo

Antworten:

144

Ich mag Joshs Antwort. Ich wollte nur eine profanere, üblichere und etwas ausführlichere Lösung hinzufügen. Ich weiß nicht, was effizienter ist. Ich gehe davon aus, dass sie eine ähnliche Leistung haben werden.

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
quelle
1
Wenn Geschwindigkeit nicht wirklich sehr wichtig ist, würde ich dies tun, da es viel verständlicher ist, was wichtig ist, wenn jemand es jemals wieder anschauen wird.
John Salvatier
6
+1 Ich habe mit Timings herumgespielt und Ihr Code scheint je nach Eingabe überraschenderweise 2-5x schneller zu sein. Ich finde die Effizienz dieser Arten von Operationen immer noch nicht intuitiv, daher lohnt es sich immer, sie timeitzu testen, wenn eine bestimmte Operation einen Engpass in Ihrem Code darstellt. Es gibt normalerweise mehr als eine Möglichkeit, Dinge in Numpy zu erledigen, also definitiv Profilcode-Schnipsel.
JoshAdel
@JoshAdel: Ich denke, wenn .reshapeeine zusätzliche Kopie des Arrays erstellt wird, würde dies einen 2x Performance-Hit erklären. Ich denke jedoch nicht, dass es immer eine Kopie macht. Ich vermute, der 5x Unterschied ist nur für kleine Arrays?
Paul
Wenn ich mir meine Lösung anschaue .flagsund .basesie teste, sieht es so aus, als würde durch die Umformung in das 'F'-Format eine versteckte Kopie der v-gestapelten Daten erstellt. Es ist also keine einfache Ansicht, wie ich es mir vorgestellt habe. Und seltsamerweise ist das 5x aus irgendeinem Grund nur für Arrays mittlerer Größe geeignet.
JoshAdel
Ein weiterer Vorteil dieser Antwort ist, dass sie nicht auf Arrays gleicher Länge beschränkt ist. Es könnte nGegenstände mit n-1Gegenständen weben .
EliadL
62

Ich dachte, es könnte sich lohnen, die Leistung der Lösungen zu überprüfen. Und das ist das Ergebnis:

Geben Sie hier die Bildbeschreibung ein

Dies zeigt deutlich, dass die am besten bewertete und akzeptierte Antwort (Pauls Antwort) auch die schnellste Option ist.

Der Code wurde von den anderen Antworten und aus genommen einem anderen Q & A :

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

Nur für den Fall, dass Sie numba zur Verfügung haben, können Sie damit auch eine Funktion erstellen:

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

Es könnte etwas schneller sein als die anderen Alternativen:

Geben Sie hier die Bildbeschreibung ein

MSeifert
quelle
2
Bemerkenswert ist auch, dass die akzeptierte Antwort viel schneller ist als eine native Python-Lösung mit roundrobin()den itertools-Rezepten.
Brad Solomon
41

Hier ist ein Einzeiler:

c = numpy.vstack((a,b)).reshape((-1,),order='F')
JoshAdel
quelle
15
Wow, das ist so unlesbar :) Dies ist einer der Fälle, in denen jemand verrückt werden kann, wenn Sie keinen richtigen Kommentar in den Code schreiben.
Ilya Kogan
9
Es sind nur zwei übliche numpy-Befehle, die aneinandergereiht sind. Ich würde nicht denken, dass es so unlesbar ist, obwohl ein Kommentar nie weh tut.
JoshAdel
1
@ JohnAdel, nun, es ist nicht numpy.vstack((a,b)).interweave():)
Ilya Kogan
6
@ Ilya: Ich hätte die Funktion .interleave()persönlich
angerufen
Was macht reshapedas
Danijel
22

Hier ist eine einfachere Antwort als einige der vorherigen

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

Danach interenthält:

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

Diese Antwort scheint auch etwas schneller zu sein:

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
quelle
10

Dies wird die beiden Arrays verschachteln / verschachteln und ich glaube, es ist gut lesbar:

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
quelle
2
Ich mag dieses als am besten lesbar. trotz der Tatsache, dass es die langsamste Lösung ist.
Kimstik
Wrap zipin einer listAbschreibung Warnung zu vermeiden
Milo Wielondek
6

Vielleicht ist dies besser lesbar als die Lösung von @ JoshAdel:

c = numpy.vstack((a,b)).ravel([-1])
Benjamin
quelle
2
ravel‚s orderArgument in der Dokumentation ist eine der C, F, A, oder K. Ich denke, Sie wollen wirklich .ravel('F'), für FORTRAN-Bestellung (Spalte zuerst)
Nick T
5

Verbesserung der Antwort von @ xioxox:

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

vstack Sicher ist eine Option, aber eine einfachere Lösung für Ihren Fall könnte die sein 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])

und was noch wichtiger ist, dies funktioniert für beliebige Formen von aundb

Vielleicht möchten Sie es auch ausprobieren dstack

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

Du hast jetzt Optionen!

Bhanukiran
quelle
7
-1 zur ersten Antwort, da die Frage nichts mit Sortieren zu tun hat. +1 bis zweite Antwort, das ist das Beste, was ich bisher gesehen habe. Aus diesem Grund sollten mehrere Lösungen als mehrere Antworten veröffentlicht werden. Bitte teilen Sie es in mehrere Antworten.
Endolith
1

Noch ein Einzeiler: np.vstack((a,b)).T.ravel()
Noch einer:np.stack((a,b),1).ravel()

Arty
quelle
0

Man kann es auch versuchen np.insert. (Lösung von Interleave-Numpy-Arrays migriert )

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)

Weitere Informationen finden Sie im documentationund tutorial.

Tai
quelle
0

Ich musste dies tun, aber mit mehrdimensionalen Arrays entlang einer beliebigen Achse. Hier ist eine schnelle Allzweckfunktion zu diesem Zweck. Es hat dieselbe Aufrufsignatur wie np.concatenate, außer dass alle Eingabearrays genau dieselbe Form haben müssen.

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
quelle