ThePatThe
ThePatThe

Reputation: 27

Numpy - Array addition using previous value

this is how I would code something, where "minute" and "errands" are lists of the same size (examples below).

But instead these are two numpy arrays, so this code doesn't work. The other thing being I'd like the result "done" to be a numpy array as well.

done = 0
for i in minute:
    if done < minute:
        done = minute + (errands * 2)
    else:
        done = done + (errands * 2)
    print (done)

So, I have also tried using "np.where"

import numpy as np
done = 0
done = np.where(done < minute, minute + (errands * 2), done + (errands * 2))
print(done)

This would be perfect, but the problem here is that it doesn't continuously update "done" so that at some point the equivalent code of "done = done + (errands * 2)" would run (if that makes sense).

Some small examples of the numpy array:

minute = np.array([2, 2, 5, 5, 6, 7, 9, 11, 15])

errands = np.array([1, 1, 1, 7, 2, 2, 1, 1, 1])

Just so I can be as clear as possible I would like the output of "done" to be

done = np.array([4, 6, 8, 22, 26, 30, 32, 34, 36])

Thanks in advance for your help.

Upvotes: 2

Views: 731

Answers (3)

max9111
max9111

Reputation: 6492

You could use Numba to accomplish this task very efficiently.

But avoid using lists whenever possible, as @user3483203 did in his answer. Lists come with a very high overhead, since Numba can't directly work on lists 2.6.2.4.1. List Reflection

Example

@nb.njit
def toggle_2(a, b):
    done=0.
    out=np.empty(a.shape[0],dtype=a.dtype)

    for i in range(a.shape[0]):
        if done < a[i]:
            done = a[i] + (b[i] * 2)
        else:
            done = done + (b[i] * 2)
        out[i]=done
    return out

Performance

e = np.random.uniform(1, 3, 1_000)
m = np.random.uniform(1, 7, 1_000).cumsum()

Paul Panzer (smart) : 13.22 µs
user3483203 (toggle): 18.47 µs
toggle_2              2.47  µs

e = np.random.uniform(1, 3, 1_000_000)
m = np.random.uniform(1, 7, 1_000_000).cumsum()

Paul Panzer (smart) : 15.97 ms
user3483203 (toggle): 30.28 ms
toggle_2              3.77  ms

Upvotes: 0

Paul Panzer
Paul Panzer

Reputation: 53089

This can be done with numpy alone:

def smart(m, e):
    e = 2*e
    r = np.empty(e.size+1, e.dtype)
    r[0] = 0
    e.cumsum(out=r[1:])
    return r[1:] + np.maximum.accumulate(m - r[:-1])

Test and timings: Set up random problem of size 1000:

>>> e = np.random.uniform(1, 3, 1000)
>>> m = np.random.uniform(1, 7, 1000).cumsum()

Gives same result as numba:

>>> np.allclose(toggle(m, e), smart(m, e))
True

But considerably faster even when compile time is excluded:

>>> timeit(lambda: toggle(m, e))
21.466296120896004
>>> timeit(lambda: smart(m, e))
11.608282678993419

Upvotes: 1

user3483203
user3483203

Reputation: 51175

This is an iterative problem because of the updating. However, it is O(n), and can be done efficiently using numba and njit:

Setup

from numba import njit

You may have to pip install numba


@njit
def toggle(a, b):
    done, out = 0, []    
    for i in range(len(a)):
        if done < a[i]:
            done = a[i] + (b[i] * 2)
        else:
            done = done + (b[i] * 2)
        out.append(done)
    return np.array(out)

toggle(minute, errands)

array([ 4,  6,  8, 22, 26, 30, 32, 34, 36], dtype=int64)

Performance

minute = np.repeat(minute, 10000)
errands = np.repeat(errands, 10000)

%timeit toggle(minute, errands)
2.02 ms ± 9.84 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit toggle_no_njit(minute, errands)
64.4 ms ± 738 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Upvotes: 2

Related Questions