2016-04-02 6 views
4

Как я могу присоединиться к двум ndarraux numpy, чтобы выполнить следующее быстрым образом, используя оптимизированный numpy, без каких-либо циклов?Объединить или объединить массивы numpy

>>> a = np.random.rand(2,2) 
>>> a 
array([[ 0.09028802, 0.2274419 ], 
     [ 0.35402772, 0.87834376]]) 

>>> b = np.random.rand(2,2) 
>>> b 
array([[ 0.4776325 , 0.73690098], 
     [ 0.69181444, 0.672248 ]]) 

>>> c = ??? 
>>> c 
array([[ 0.09028802, 0.2274419, 0.4776325 , 0.73690098], 
     [ 0.09028802, 0.2274419, 0.69181444, 0.672248 ], 
     [ 0.35402772, 0.87834376, 0.4776325 , 0.73690098], 
     [ 0.35402772, 0.87834376, 0.69181444, 0.672248 ]]) 
+1

'np.hstack ((а [[0, 0, 1, 1]], б [[0 , 1, 0, 1]]), будет делать это. Предположительно, кто-то может придумать более общий способ сделать это. – Paul

ответ

2

Let Прогулка по перспективному решению для обработки типичных случаев с использованием различных форм-массивов с некоторыми встроенными комментариями для объяснения используемого метода.

(1) Во-первых, мы храним формы входных массивов.

ma,na = a.shape 
mb,nb = b.shape 

(2) Далее, инициализировать 3D-массив с числом столбцов, являющихся суммой числа столбцов во входных массивах a и b. Используйте для этого задания np.empty.

out = np.empty((ma,mb,na+nb),dtype=a.dtype) 

(3) Затем установить первую ось 3D массива для первого «NA» колонны с рядами из a с a[:,None,:]. Итак, если мы назначим его out[:,:,:na], этот второй двоеточие укажет NumPy, что нам потребуется широковещательная настройка, если это возможно, как всегда, с синглетовыми dims в массивах NumPy. По сути, это было бы так же, как черепица/повторение, но, возможно, эффективным способом.

out[:,:,:na] = a[:,None,:] 

(4) Повторите для установки элементов из b в выходной массив. На этот раз мы будем транслировать по первой оси out с out[:,:,na:], причем эта первая двоеточие помогает нам делать это вещание.

out[:,:,na:] = b 

(5) Конечный шаг состоит в том, чтобы преобразовать выход в двумерную форму. Это можно сделать, просто изменив форму с помощью требуемого двустрочного кортежа. Перестановка только изменений зрения и фактически нулевой стоимости.

out.shape = (ma*mb,na+nb) 

конденсаторные все, в полном объеме будет выглядеть следующим образом -

ma,na = a.shape 
mb,nb = b.shape 
out = np.empty((ma,mb,na+nb),dtype=a.dtype) 
out[:,:,:na] = a[:,None,:] 
out[:,:,na:] = b 
out.shape = (ma*mb,na+nb) 
+1

Не очень красивый лайнер, но быстрее. – hpaulj

+0

@hpaulj Иногда уродливые бывают быстрыми :) – Divakar

+0

@Divakar Я очень удивлен, что это работает. Я не понимал, что вы можете просто назначить кортеж 'shape'. Можете ли вы точно объяснить, что происходит? – Paul

-1

Все массивы индексируемых, так что вы можете слить, просто позвонив:

a[:2],b[:2] 

или вы можете использовать основной NumPy штабелирования функции, должны выглядеть примерно так:

c = np.vstack(a,b) 
+0

Первое, что просто создает 2-племя. Второй не работает, но даже если бы это было так, это просто добавило бы b в a. OP хочет, чтобы массив numpy был декартовым произведением двух массивов. – Paul

3

Вы хотите, по-видимому, декартово произведение a и b, уложенное горизонтально. Вы можете использовать itertools модуль для создания индексов для Numpy массивов, то numpy.hstack укладывать их:

import numpy as np 
from itertools import product 

a = np.array([[ 0.09028802, 0.2274419 ], 
       [ 0.35402772, 0.87834376]]) 

b = np.array([[ 0.4776325 , 0.73690098], 
       [ 0.69181444, 0.672248 ], 
       [ 0.79941110, 0.52273 ]]) 

a_inds, b_inds = map(list, zip(*product(range(len(a)), range(len(b))))) 

c = np.hstack((a[a_inds], b[b_inds])) 

Это приводит к c из:

array([[ 0.09028802, 0.2274419 , 0.4776325 , 0.73690098], 
     [ 0.09028802, 0.2274419 , 0.69181444, 0.672248 ], 
     [ 0.09028802, 0.2274419 , 0.7994111 , 0.52273 ], 
     [ 0.35402772, 0.87834376, 0.4776325 , 0.73690098], 
     [ 0.35402772, 0.87834376, 0.69181444, 0.672248 ], 
     [ 0.35402772, 0.87834376, 0.7994111 , 0.52273 ]]) 

ломают индексы вещь:

product(range(len(a)), range(len(b)) будет генерировать что-то похожее, если вы преобразуете его в список:

[(0, 0), (0, 1), (1, 0), (1, 1)] 

Вы хотите что-то вроде этого: [0, 0, 1, 1], [0, 1, 0, 1], поэтому вам нужно транспонировать генератор. Идиоматический способ сделать это - zip(*zipped_thing). Однако, если вы только непосредственно назначить их, вы получите tuples, как это:

[(0, 0, 1, 1), (0, 1, 0, 1)] 

Но Numpy массивов интерпретировать как кортежи многомерных индексов, так что вы хотите, чтобы превратить их в списки, поэтому я сопоставляюсь конструктор list на результат функции product.

3

Не хорошенькая, но вы могли бы объединить hstack, repeat и tile:

>>> a = np.arange(4).reshape(2,2) 
>>> b = a+10 
>>> a 
array([[0, 1], 
     [2, 3]]) 
>>> b 
array([[10, 11], 
     [12, 13]]) 
>>> np.hstack([np.repeat(a,len(a),0),np.tile(b,(len(b),1))]) 
array([[ 0, 1, 10, 11], 
     [ 0, 1, 12, 13], 
     [ 2, 3, 10, 11], 
     [ 2, 3, 12, 13]]) 

Или для 3x3 случая:

>>> a = np.arange(9).reshape(3,3) 
>>> b = a+10 
>>> np.hstack([np.repeat(a,len(a),0),np.tile(b,(len(b),1))]) 
array([[ 0, 1, 2, 10, 11, 12], 
     [ 0, 1, 2, 13, 14, 15], 
     [ 0, 1, 2, 16, 17, 18], 
     [ 3, 4, 5, 10, 11, 12], 
     [ 3, 4, 5, 13, 14, 15], 
     [ 3, 4, 5, 16, 17, 18], 
     [ 6, 7, 8, 10, 11, 12], 
     [ 6, 7, 8, 13, 14, 15], 
     [ 6, 7, 8, 16, 17, 18]]) 
+0

Это замечательно. Большое спасибо. не задал в моем вопросе, но если a и b - разные размеры, ваше решение генерирует ошибку. Следующее изменение исправляет этот случай: 'np.hstack ([np.repeat (a, len (b), 0), np.tile (b, (len (a), 1))])' –

+0

Собственно, мое выше предложение не работает. Но попробуйте с помощью 'b = np.random.rand (3,2)' Я вижу 'ValueError: все размеры входного массива, кроме оси конкатенации, должны точно соответствовать –

+0

@HelloWorld: выше было сделано в предположении, что' a 'и' b' были квадратными и одинаковыми, как в вашем примере. Вам нужно справиться с общим случаем? – DSM

0

Вы можете использовать dstack() и broadcast_arrays():

import numpy as np 

a = np.random.randint(0, 10, (3, 2)) 
b = np.random.randint(10, 20, (4, 2)) 

np.dstack(np.broadcast_arrays(a[:, None], b)).reshape(-1, a.shape[-1] + b.shape[-1]) 
+0

Это использование 'broadcast_arrays' умное, но медленнее, чем с' repeat' и 'tile'. – hpaulj

Смежные вопросы