Переплетення двох масивів numpy


84

Припустимо, що наведені такі масиви:

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

Як можна їх ефективно переплести, щоб отримати такий третій масив

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

Можна припустити, що length(a)==length(b).


1
Як щодо того самого питання, але ви намагаєтесь переплести матриці. Тобто a і b є тривимірними, і не обов'язково однакового розміру в першому вимірі. Примітка: Перемішувати слід лише перший вимір.
Джеронімо

Відповіді:


144

Мені подобається відповідь Джоша. Я просто хотів додати більш повсякденне, звичне і трохи більш багатослівне рішення. Я не знаю, що є більш ефективним. Я сподіваюся, вони будуть мати подібні показники.

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
Якщо швидкість насправді не дуже важлива, я б погодився з цим, оскільки це набагато зрозуміліше, що важливо, якщо хтось коли-небудь збиратиметься поглянути на неї ще раз.
Джон Сальватьє,

6
+1 Я погрався з таймінгами, і ваш код на диво здається в 2-5 разів швидший залежно від введених даних. Я все ще вважаю ефективність цих типів операцій неінтуїтивною, тому завжди варто використовувати, timeitщоб перевірити ситуацію, якщо певна операція є вузьким місцем у вашому коді. Зазвичай існує кілька способів робити щось у numpy, тому, безперечно, фрагменти коду профілю.
JoshAdel

@JoshAdel: Думаю, якщо .reshapeстворить додаткову копію масиву, це пояснювало б збільшення продуктивності у 2 рази. Однак я не думаю, що це завжди робить копію. Я здогадуюсь, що різниця в 5 разів стосується лише малих масивів?
Пол

дивлячись .flagsі тестуючи .baseмоє рішення, схоже, що зміна формату на формат F створює приховану копію даних vstacked, тому це не простий вигляд, як я думав, буде. І дивно, що 5x чомусь призначений лише для масивів середнього розміру.
JoshAdel

Ще однією перевагою цієї відповіді є те, що вона не обмежується масивами однакової довжини. Він міг плести nпредмети з n-1предметами.
EliadL

62

Я вважав, що варто перевірити, як працюють рішення з точки зору продуктивності. І ось результат:

введіть тут опис зображення

Це наочно показує, що найбільш схвалена та прийнята відповідь (відповідь Паулса) - це також найшвидший варіант.

Код взятий з інших відповідей та з інших запитань :

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

Про всяк випадок, якщо у вас є numba, ви також можете використовувати це для створення функції:

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

Це може бути трохи швидше, ніж інші альтернативи:

введіть тут опис зображення


2
Також слід зазначити, що прийнята відповідь набагато швидша, ніж власне рішення Python із roundrobin()рецептів itertools.
Бред Соломон,

41

Ось однокласний вкладиш:

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

16
Ого, це так нечитабельно :) Це один із випадків, коли, якщо ви не напишете належного коментаря в коді, це може звести когось з розуму.
Ілля Коган

9
Це лише дві загальні команди numpy, зв’язані між собою. Я не думаю, що це так нечитабельно, хоча коментар ніколи не заважає.
JoshAdel

1
@JohnAdel, ну це не numpy.vstack((a,b)).interweave():)
Ілля Коган

6
@Ilya: Я би зателефонував функції .interleave()особисто :)
JoshAdel

Що робить reshape?
Даніель

23

Ось простіша відповідь, ніж деякі з попередніх

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

Після цього interміститься:

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

Ця відповідь також виявляється незначно швидшою:

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

Це дозволить чергувати / чергувати два масиви, і я вважаю, що це цілком читабельно:

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
Мені подобається цей як найбільш читабельний. незважаючи на те, що це найповільніше рішення.
kimstik

Загорніть zipу, listщоб уникнути попередження про амортизацію
Майло Вілондек,

6

Можливо, це читабельніше, ніж рішення @ JoshAdel:

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

2
ravel«S orderаргумент в документації є одним з C, F, Aабо K. Думаю, ви дійсно хочете .ravel('F'), для замовлення FORTRAN (перша колонка)
Нік Т

5

Покращення відповіді @ 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 Звичайно, це варіант, але більш простим рішенням для вашої справи може бути 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іb

Також ви можете спробувати dstack

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

у вас зараз є варіанти!


7
-1 до першої відповіді, оскільки питання не має нічого спільного з сортуванням. +1 до другої відповіді, яка є найкращою, яку я бачив на сьогодні. Ось чому кілька рішень слід розміщувати як кілька відповідей. Будь ласка, розділіть його на кілька відповідей.
ендоліт

1

Ще один однокласник: np.vstack((a,b)).T.ravel()
Ще один:np.stack((a,b),1).ravel()


0

Можна також спробувати np.insert. (Рішення перенесено з масивів 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)

Будь ласка , дивіться documentationі tutorialдля отримання додаткової інформації.


0

Мені потрібно було це зробити, але з багатовимірними масивами вздовж будь-якої осі. Ось швидка загальна функція для цього. Він має той самий підпис виклику np.concatenate, за винятком того, що всі вхідні масиви повинні мати абсолютно однакову форму.

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.