Przeplatanie dwóch numpy tablic


84

Załóżmy, że podane są następujące tablice:

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

Jak skutecznie je przeplatać, aby otrzymać trzecią tablicę, taką jak ta

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

Można przypuszczać, że length(a)==length(b).


1
Co powiesz na to samo pytanie, ale próbujesz przeplatać macierze. Oznacza to, że a i b są trójwymiarowe i niekoniecznie są tego samego rozmiaru w pierwszym wymiarze. Uwaga: tylko pierwszy wymiar powinien być przeplatany.
Geronimo

Odpowiedzi:


145

Podoba mi się odpowiedź Josha. Chciałem tylko dodać bardziej przyziemne, zwykłe i nieco bardziej szczegółowe rozwiązanie. Nie wiem, który jest bardziej wydajny. Spodziewam się, że będą miały podobną wydajność.

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
O ile szybkość nie jest naprawdę ważna, podjąłbym się tego, ponieważ jest ona znacznie bardziej zrozumiała, co jest ważne, jeśli ktoś kiedykolwiek ma na to spojrzeć.
John Salvatier,

6
+1 Bawiłem się synchronizacjami i Twój kod zaskakująco wydaje się być 2-5x szybszy w zależności od danych wejściowych. Nadal uważam, że wydajność tego typu operacji jest nieintuicyjna, więc zawsze warto ją wykorzystać timeitdo przetestowania rzeczy, jeśli dana operacja jest wąskim gardłem w kodzie. Zwykle istnieje więcej niż jeden sposób robienia rzeczy w numpy, więc zdecydowanie fragmenty kodu profilu.
JoshAdel

@JoshAdel: Wydaje mi się, że jeśli .reshapetworzy dodatkową kopię tablicy, to wyjaśniałoby to dwukrotne uderzenie w wydajność. Nie sądzę jednak, że zawsze tworzy kopię. Zgaduję, że różnica 5x dotyczy tylko małych tablic?
Paul

patrząc .flagsi testując .basemoje rozwiązanie, wygląda na to, że zmiana kształtu na format `` F '' tworzy ukrytą kopię danych w stosie vstack, więc nie jest to prosty widok, jak myślałem. O dziwo, 5x jest z jakiegoś powodu tylko dla tablic o średniej wielkości.
JoshAdel

Kolejną zaletą tej odpowiedzi jest to, że nie ogranicza się ona do tablic o tej samej długości. Może tkać nprzedmioty z n-1przedmiotami.
EliadL

63

Pomyślałem, że warto sprawdzić, jak te rozwiązania sprawdzają się pod względem wydajności. A oto wynik:

wprowadź opis obrazu tutaj

To wyraźnie pokazuje, że najbardziej pozytywna i zaakceptowana odpowiedź (odpowiedź Paulsa) jest również najszybszą opcją.

Kod został wzięty z innych odpowiedzi oraz z innego pytania i odpowiedzi :

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

Na wypadek, gdybyś miał dostępny numba, możesz również użyć tego do stworzenia funkcji:

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

Może być nieco szybszy niż inne alternatywy:

wprowadź opis obrazu tutaj


2
Warto również zauważyć, że zaakceptowana odpowiedź jest znacznie szybsza niż natywne rozwiązanie Pythona z roundrobin()recepturami z itertools.
Brad Solomon

42

Oto jedna linijka:

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

17
Wow, to jest takie nieczytelne :) To jeden z przypadków, w których jeśli nie napiszesz odpowiedniego komentarza w kodzie, może to doprowadzić kogoś do szaleństwa.
Ilya Kogan,

10
To tylko dwa typowe polecenia numpy połączone razem. Nie sądziłbym, że jest to tak nieczytelne, chociaż komentarz nigdy nie boli.
JoshAdel

1
@JohnAdel, no cóż, nie jest numpy.vstack((a,b)).interweave():)
Ilya Kogan

6
@Ilya: Zadzwoniłbym do tej funkcji .interleave()osobiście :)
JoshAdel

Co robi reshape?
Danijel

23

Oto prostsza odpowiedź niż niektóre poprzednie

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

Po tym interzawiera:

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

Ta odpowiedź również wydaje się być nieznacznie szybsza:

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

Spowoduje to przeplot / przeplot dwóch tablic i uważam, że jest całkiem czytelny:

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
Podoba mi się ten jako najbardziej czytelny. mimo że jest to najwolniejsze rozwiązanie.
kimstik

Wrap zipw sposób listuniknąć ostrzeżenie amortyzacji
Milo Wielondek

6

Może jest to bardziej czytelne niż rozwiązanie @ JoshAdel:

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

2
ravel„s orderargumentem w dokumentacji jest jednym z C, F, A, lub K. Myślę, że naprawdę chcesz .ravel('F'), dla zamówienia FORTRAN (pierwsza kolumna)
Nick T

5

Poprawa odpowiedzi @ 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 Oczywiście jest opcją, ale prostszym rozwiązaniem w Twoim przypadku może być 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])

a co ważniejsze, działa to dla dowolnych kształtów aib

Możesz także spróbować dstack

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

masz teraz opcje!


7
-1 do pierwszej odpowiedzi, ponieważ pytanie nie ma nic wspólnego z sortowaniem. +1 do drugiej odpowiedzi, która jest najlepsza, jaką do tej pory widziałem. Dlatego wiele rozwiązań powinno być publikowanych jako wiele odpowiedzi. Podziel go na kilka odpowiedzi.
endolith

1

Musiałem to zrobić, ale z wielowymiarowymi tablicami wzdłuż dowolnej osi. Oto szybka funkcja ogólnego przeznaczenia do tego celu. Ma taką samą sygnaturę wywołania jak np.concatenate, z tym wyjątkiem, że wszystkie tablice wejściowe muszą mieć dokładnie ten sam kształt.

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 dla takiej uogólnionej reguły (działa z n-dim, przeplata wzdłuż dowolnej osi, działa dla dowolnej liczby tablic wejściowych, przyjmuje opcjonalny outargument i działa dla tablic z podklasami). Osobiście wolałbym raczej axisdomyślnie -1niż domyślnie 0, ale może to tylko ja. I możesz chcieć połączyć się z tą twoją odpowiedzią, z tego pytania , które w rzeczywistości wymagało, aby tablice wejściowe były n-wymiarowe.
fontanna


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.