2015-07-30 10 views
2

Я пытаюсь найти способ для векторизации операции, в которой я беру 1 массив numpy и разворачиваю каждый элемент на 4 новые точки. Я сейчас делаю это с петлей Python. Сначала позвольте мне объяснить алгоритм.Векторизовать расширение массива numpy

input_array = numpy.array([1, 2, 3, 4]) 

Я хочу «развернуть» или «расширить» каждый элемент в этом массиве до 4 точек. Таким образом, элемент ноль (значение 1) будет расширено до этих 4-й точек:

[0, 1, 1, 0] 

Это случилось бы для каждого элемента, чтобы в конечном итоге с окончательным массивом:

[0, 1, 1, 0, 0, 2, 2, 0, 0, 3, 3, 0, 0, 4, 4, 0] 

я хотел бы сделайте код немного общим, чтобы я мог также выполнить это «расширение» по-другому. Например:

input_array = numpy.array([1, 2, 3, 4]) 

На этот раз каждая точка расширяется путем добавления + = .2 к каждой точке. Итак, окончательный массив будет:

[.8, .8, 1.2, 1.2, 1.8, 1.8, 2.2, 2.2, 2.8, 2.8, 3.2, 3.2, 3.8, 3.8, 4.2, 4.2] 

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

output = [] 
for x in input_array: 
    output.append(expandPoint(x)) 

output = numpy.concatenate(output) 

def expandPoint(x): 
    return numpy.array([0, x, x, 0]) 

def expandPointAlternativeStyle(x): 
    return numpy.array([x - .2, x - .2, x + .2, x + .2]) 

ответ

1

Кажется, что вы хотите чтобы делать операции с элементами по порядку между input_array и массивом, содержащим расширяющиеся элементы. Для этого вы можете использовать broadcasting.

Для первого примера, кажется, вы выполняете elementwise multiplication -

In [424]: input_array = np.array([1, 2, 3, 4]) 
    ...: extend_array = np.array([0, 1, 1, 0]) 
    ...: 

In [425]: (input_array[:,None] * extend_array).ravel() 
Out[425]: array([0, 1, 1, 0, 0, 2, 2, 0, 0, 3, 3, 0, 0, 4, 4, 0]) 

Для второго примера, кажется, вы выполняете elementwise addition -

In [422]: input_array = np.array([1, 2, 3, 4]) 
    ...: extend_array = np.array([-0.2, -0.2, 0.2, 0.2]) 
    ...: 

In [423]: (input_array[:,None] + extend_array).ravel() 
Out[423]: 
array([ 0.8, 0.8, 1.2, 1.2, 1.8, 1.8, 2.2, 2.2, 2.8, 2.8, 3.2, 
     3.2, 3.8, 3.8, 4.2, 4.2]) 
+0

А, я никогда не думал об использовании умножения и дополнения. Теперь кажется очевидным, что я это вижу. Благодаря! –

+0

Этот подход и подход 'ufunc' с« внешним »кажутся эквивалентными, но я пойду с этим, поскольку он был первым. Я тестировал оба, и они кажутся одинаковыми с точки зрения производительности. –

1

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

input_array = np.array([1, 2, 3, 4]) 
template = np.array([0, 1, 1, 0]) 

np.multiply.outer(input_array, template) 
# array([[0, 1, 1, 0], 
#  [0, 2, 2, 0], 
#  [0, 3, 3, 0], 
#  [0, 4, 4, 0]]) 

result = np.multiply.outer(input_array, template).ravel() 
# array([0, 1, 1, 0, 0, 2, 2, 0, 0, 3, 3, 0, 0, 4, 4, 0]) 

Аналогично для второго примера можно использовать np.add.outer

np.add.outer(input_array, [-0.2, -0.2, 0.2, 0.2]).ravel() 
# array([ 0.8, 0.8, 1.2, 1.2, 1.8, 1.8, 2.2, 2.2, 2.8, 2.8, 3.2, 
     3.2, 3.8, 3.8, 4.2, 4.2]) 

См:

+0

спасибо за ссылку. Это выглядит так, как будто это сработает и кажется самым прямым. –

+0

Этот пример выглядит так: использование внешних вызовов является прямым эквивалентом подхода к широковещанию ниже от @divakar. Интересно, существует ли конвенция для использования одного над другим? –

+0

Да, они эквивалентны «np.multiply» и «np.add» являются «ufuncs», которые могут позаботиться о вещании для вас - см. Http://docs.scipy.org/doc/numpy/reference/ufuncs.html – YXD

2

Для первого примера, вы можете использовать np.kron

>>> a = np.array([0, 1, 1, 0]) 
>>> np.kron(input_array, a) 
array([0, 1, 1, 0, 0, 2, 2, 0, 0, 3, 3, 0, 0, 4, 4, 0]) 

Для второго примера, вы можете использовать np.repeat и np.tile

>>> b = np.array([-0.2, -0.2, 0.2, 0.2]) 
>>> np.repeat(input_array, b.size) + np.tile(b, input_array.size) 
array([ 0.8, 0.8, 1.2, 1.2, 1.8, 1.8, 2.2, 2.2, 2.8, 2.8, 3.2, 
     3.2, 3.8, 3.8, 4.2, 4.2]) 
+0

Это полезно знать! Однако в моем сценарии это не сработает, потому что входные номера повсюду. Они не являются «регулярными», как 1, 2, 3, 4. Я просто использовал это в качестве примера. –

+0

Что значит «повсюду»? – yangjie

+0

Я имею в виду не предсказуемый, плохой выбор слов с моей стороны. Я посмотрел на «железо», потому что раньше я его не видел, и похоже, что он сработает. Ранее я думал, что ваше решение связано с цифрами 0 и 1, я ошибался. –

2

Я не уверен логики вашего алгоритма, но я думаю, если вы хотите, чтобы каждая точка была extend, а затем оставьте их в очередь, ваш лучший подход - увеличить размерность, а затем взять сглаженную версию; для первого примера:

>>> x = np.array([1,2,3,4]) 
>>> x 
array([1, 2, 3, 4]) 
>>> y = np.empty((len(x), 4)) 
>>> y[:, [0, 3]] = 0 
>>> y[:, 1:3] = x[:, None] 
>>> y 
array([[ 0., 1., 1., 0.], 
     [ 0., 2., 2., 0.], 
     [ 0., 3., 3., 0.], 
     [ 0., 4., 4., 0.]]) 
>>> y.reshape((4*len(x),)) # Flatten it back 
array([ 0., 1., 1., 0., 0., 2., 2., 0., 0., 3., 3., 0., 0., 
    4., 4., 0.]) 

Как вы затем идти о том, что родовое зависит от алгоритма, который я не совсем уверен следовать ... Но это должно дать вам несколько советов, чтобы начать работу.

Редактировать: Как уже было сказано, вы можете сделать все это с помощью внешних продуктов гораздо более сжатым способом, который, вероятно, будет более точно соответствовать вашему алгоритму, например., Shamelessely делает YXD ответить на один-лайнер:

>>> (x[:, None] * np.array([0,1,1,0])[None, :]).flatten() 
array([0, 1, 1, 0, 0, 2, 2, 0, 0, 3, 3, 0, 0, 4, 4, 0]) 

Однако принцип остается идти в более высокой размерности (2), прежде чем расширять его в вас оригинальный размер (1)

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