pairwise traversal of a list or tuple
I would recommend to use awesome more_itertools library, it has ready-to-use pairwise function:
import more_itertools
for a, b in more_itertools.pairwise([1, 2, 3, 4, 5]):
print(a, b)
# 1 2
# 2 3
# 3 4
# 4 5
It will save you from writing your own (likely buggy) implementation. For example, most of implementations on this page don't handle the case with empty iterable correctly -- generator function should never raise StopIteration
, this behavior considered deprecated and causes DeprecationWarning in Python 3.6. It won't work in Python 3.7 at all.
Here is the example from the itertools reciepes:
from itertools import tee
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
Which is not very readable.If you prefer something more understandable and understand how generators work, here a bit longer example with the same result:
def pairwise(it):
"""
Walk a list in overlapping pairs.
>>> list(pairwise([0, 1, 2, 3, 4, 5]))
[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
"""
it = iter(it)
start = None
while True:
if not start:
start = next(it)
end = next(it)
yield start, end
start = end
for a small list in python 2 or any list in python 3, you can use
[x - y for x, y in zip(a[1:], a)]
for a larger list, you probably want
import itertools as it
[x - y for x, y in it.izip(a[1:], a)]
if you are using python 2
And I would consider writing it as a generator expression instead
(x - y for x, y in it.izip(a[1:], a))
This will avoid creating the second list in memory all at once but you will only be able to iterate over it once. If you only want to iterate over it once, then this is ideal and it's easy enough to change if you decide later that you need random or repeated access. In particular if you were going to further process it to make a list, then this last option is ideal.
update:
The fastest method by far is
import itertools as it
import operator as op
list(it.starmap(op.sub, it.izip(a[1:], a)))
$ python -mtimeit -s's = [1, 2]*10000' '[x - y for x, y in zip(s[1:], s)]'
100 loops, best of 3: 13.5 msec per loop
$ python -mtimeit -s'import itertools as it; s = [1, 2]*10000' '[x - y for x, y in it.izip(s[1:], s)]'
100 loops, best of 3: 8.4 msec per loop
$ python -mtimeit -s'import itertools as it; import operator as op; s = [1, 2]*10000' 'list(it.starmap(op.sub, it.izip(s[1:], s)))'
100 loops, best of 3: 6.38 msec per loop
It's ok to use range
. However, programming (like maths) is about building on abstractions. Consecutive pairs [(x0, x1), (x1, x2), ..., (xn-2, xn-1)], are called pairwise combinations. See an example in the itertools docs. Once you have this function in your toolset, you can write:
for x, y in pairwise(xs):
print(y - x)
Or used as a generator expression:
consecutive_diffs = (y - x for (x, y) in pairwise(xs))