Reputation: 580
I'm writing a function which does the following:
for a given list: X, return a list of x[1] - x[0], x[2] - (x[1] - x[0]), x[3] - ( x[2] - (x[1] - x[0]) and so on.
I have a for loop which achieves this perfectly fine but was curious if there was a better way to do this? I'm conscience that my list X can be very large, so speed and efficiency is paramount.
test = [25, 30, 50, 60, 100, 1000, 100000]
diff = test[0]
output = []
for i in range(len(test) -1):
x1 = test[i + 1] - diff
output.append(x1)
diff = x1
output
Thanks!
Upvotes: 0
Views: 494
Reputation: 195468
There's minor speed-up with itertools.accumulate
:
i = accumulate(test, lambda acc, v: v - acc)
next(i)
output = [*i]
print(output)
Prints:
[5, 45, 15, 85, 915, 99085]
Benchmark (using one million item list with ascending elements):
from random import randint
from itertools import accumulate
from timeit import timeit
test = sorted([randint(10, 100_000) for _ in range(1_000_000)])
def f1():
diff = test[0]
output = []
for i in range(len(test) -1):
x1 = test[i + 1] - diff
output.append(x1)
diff = x1
return output
def f2():
i = accumulate(test, lambda acc, v: v - acc)
next(i)
output = [*i]
return output
t1 = timeit(lambda: f1(), number=1)
t2 = timeit(lambda: f2(), number=1)
print(t1)
print(t2)
Prints on my machime (AMD 2400G, Python 3.8):
0.23373416997492313
0.19420667097438127
Upvotes: 1
Reputation: 1808
Using numpy (hopefully that's ok):
output = list(np.cumsum((-1) ** np.arange(len(test)) * test)[1:] * (-1) ** np.arange(1,len(test)))
It's probably better to split into a couple of lines:
r = (-1) ** np.arange(len(test))
output = list(np.cumsum(r * test)[1:] * r[1:])
From your definition of the outputs:
x[1]-x[0],
x[2] - (x[1]-x[0]) = x[2] - x[1] + x[0],
x[3] - (x[2]-(x[1]-x[0]) = x[3] - x[2] + x[1] - x[0]
...
So this just exploits the fact that you want the cumulative sum of the numbers in test
, alternating in whether they are positive or negative.
Upvotes: 0