2016-12-20 12 views
2

Мне нужно выполнить логическую итерацию по массиву numpy, значения которой зависят от элементов другого массива. Я написал код ниже для выяснения моей проблемы. Любые предложения по решению этой проблемы без цикла?Итерация без цикла в массиве numpy

Code 
a = np.array(['a', 'b', 'a', 'a', 'b', 'a']) 
b = np.array([150, 154, 147, 126, 148, 125]) 
c = np.zeros_like(b) 
c[0] = 150 
for i in range(1, c.size): 
    if a[i] == "b": 
     c[i] = c[i-1] 
    else: 
     c[i] = b[i] 

ответ

2

Вот подход, использующий сочетание np.maximum.accumulate и np.where создать ступенчатые показатели, которые должны быть остановлены через определенные промежутки времени, а затем просто индексации в b даст нам желаемый результат.

Таким образом, реализация будет -

mask = a!="b" 
idx = np.maximum.accumulate(np.where(mask,np.arange(mask.size),0)) 
out = b[idx] 

Пример шаг за шагом пробег -

In [656]: # Inputs 
    ...: a = np.array(['a', 'b', 'a', 'a', 'b', 'a']) 
    ...: b = np.array([150, 154, 147, 126, 148, 125]) 
    ...: 

In [657]: mask = a!="b" 

In [658]: mask 
Out[658]: array([ True, False, True, True, False, True], dtype=bool) 

# Crux of the implmentation happens here : 
In [696]: np.where(mask,np.arange(mask.size),0) 
Out[696]: array([0, 0, 2, 3, 0, 5]) 

In [697]: np.maximum.accumulate(np.where(mask,np.arange(mask.size),0)) 
Out[697]: array([0, 0, 2, 3, 3, 5])# Stepped indices "intervaled" at masked places 

In [698]: idx = np.maximum.accumulate(np.where(mask,np.arange(mask.size),0)) 

In [699]: b[idx] 
Out[699]: array([150, 150, 147, 126, 126, 125]) 
1

Вы могли бы использовать более векторизованную подход Как так:

np.where(a == "b", np.roll(c, 1), b) 

np.where примет элементы от np.roll(c, 1), если условие True или он будет принимать от b, если условие False. np.roll(c, 1) будет «катить» вперед все элементы c на 1, чтобы каждый элемент ссылался на c[i-1].

Этот тип операций - это то, что делает numpy настолько бесценным. Следует избегать петли, если это возможно.

+0

Это простое и краткое решение. Но почему он возвращает [150 0 147 126 0 125]? Он не принимает значения из массива «b», где a [i] = «b». –

+0

Я неправильно понял вопрос, это работает только в том случае, если у вас уже есть все элементы 'c', но вы заполняете его через цикл, поэтому он делает его немного сложнее, чем это – pbreach

0

Если вам не нужно обернуть вокруг края есть очень просто решение:

a = np.array(['a', 'b', 'a', 'a', 'b', 'a']) 
b = np.array([150, 154, 147, 126, 148, 125]) 
c = b.copy() #removes necessity of else case 
c[a[:-1]=='b'] = c[a[1:]=='b'] 

или одинаково:

a = np.array(['a', 'b', 'a', 'a', 'b', 'a']) 
b = np.array([150, 154, 147, 126, 148, 125]) 
c = b.copy() #removes necessity of else case 
mask = a == 'b' 
c[mask[:-1]] = c[mask[1:]] 

Если вы хотите, чтобы обернуть вокруг края (a[0]=='b') то это становится немного сложнее, вам либо нужно использовать roll, либо сначала поймать этот случай, и if.