2016-09-20 2 views
-1

Есть ли какой-нибудь способ, чтобы избежать for цикла в этом коде:NumPy: Limited накопленная сумма

X = numpy array ~8000 long 
running_total = 0 
for x in X: 
    this_step = min(x, running_total) 
    running_total += this_step 

В словах, что это расчетливый кумулятивную сумму ряда, где разница между образцами ограничивается предыдущим значением от совокупной суммы. Есть ли способ сделать это с помощью элементарных операций numpy? Или я должен искать запись функции C, если мне будет интересно, как быстро это работает?

Редактировать Очевидно, я не прояснил ситуацию. Моя попытка упростить код выше, похоже, вызвала больше путаницы, чем что-либо еще. Вот что-то ближе к реальному коду:

monthly_income = numpy array ~ 8000 long 
monthly_expenditure = numpy array ~ 8000 long 
running_credit = numpy.zeros(len(monthly_income) + 1) 
monthly_borrowing = numpy.zeros(len(monthly_income)) 
for index, i, e in zip(range(len(monthly_income)), monthly_income, monthly_expenditure): 
    assets_used = max(0, e - i) 
    assets_used = min(assets_used, running_credit[index]) 
    monthly_borrowing[index] = max(0, e - i - running_credit[index]) 
    running_credit[index+1] += max(0, i - e) - assets_used 

Дело в том, что running_index[index+1] зависит от assets_used в образце index, который зависит от running_credit[index]. Выполнение этого в цикле Python происходит медленно - в функции, которая выполняет множество аналогичных вычислений на одних и тех же входных массивах, используя операции NumPy, вышеуказанный цикл занимает более 80% времени выполнения. Но я не вижу способа выполнить описанную выше операцию без цикла for.

Итак, есть ли способ сделать эту итеративную операцию в NumPy без цикла for? Или мне нужно написать функцию C, если я хочу, чтобы это быстро запускалось?

+4

Если 'this_step' неотрицательна,' running_total' всегда 0. На самом деле, я не могу найти использование 'x' и' y' внутри для цикла и, кажется, либо код или описание не может объяснить другой не очень хорошо. – Jeon

+0

Извините, он отредактирован с большого кода. Исправлено. – Tom

+2

Я не уверен, что ваш код соответствует вашим объяснениям. В частности, 'running_total' всегда будет неположительным в соответствии с вашим кодом. –

ответ

0

Не уверен, но предполагаю, из вашего объяснения (при условии х неотрицательна)

X = [1 ... 999] 
running_total = X[0] 
for x in X[1:]: 
    this_step = min(x, running_total) 
    running_total += this_step 
+0

Вопрос был: «Есть ли способ сделать это, используя операции с элементами numpy?» Вы все еще используете for-loop. – Tom

+0

* Следующий * 'running_total' не определен, если не указано * предыдущее *. Итак, 'this_step'. Элементная операция действительна, когда каждый элемент не тронут другими. – Jeon

0

Самый простой быстро исправить для такого рода проблемы я найти это использовать Numba. Например.

from numba import jit 
import numpy as np 

def cumsumcapped(X): 
    running_total = 0 
    for x in X: 
     this_step = min(x, running_total) 
     running_total += this_step 

@jit 
def cumsumcappedjit(X): 
    running_total = 0 
    for x in X: 
     this_step = min(x, running_total) 
     running_total += this_step 

X = np.random.randint(1, 100, 10000) 

In [5]: %timeit cumsumcapped(X) 
100 loops, best of 3: 4.1 ms per loop 

In [6]: %timeit stack.cumsumcappedjit(X) 
The slowest run took 170143.03 times longer than the fastest. This  could mean that an intermediate result is being cached. 
1000000 loops, best of 3: 587 ns per loop