Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

adjacent differences with a list comprehension

0 views
Skip to first unread message

Phil Schmidt

unread,
Mar 24, 2003, 4:31:16 PM3/24/03
to
Given a list of numbers, such as:

L = [2, 5, 8, 3, 9, 1]

I want to generate a list containing the differences between adjacent
elements, i.e.,

Ld = [3, 3, -5, 6, -8]

I don't see how I can do this (easily/elegantly) with list
comprehensions.

Any suggestions? Thanks!

Cliff Wells

unread,
Mar 24, 2003, 4:46:54 PM3/24/03
to

[j - i for i, j in zip(L, L[1:])]


--
Cliff Wells, Software Engineer
Logiplex Corporation (www.logiplex.net)
(503) 978-6726 x308 (800) 735-0555 x308


Sean Ross

unread,
Mar 24, 2003, 5:02:44 PM3/24/03
to
This will do what you're looking for:

def adjacentdiffs(seq):
return [ x - y for x, y in zip (seq[1:], seq[:-1]) ]

Sean

"Phil Schmidt" <psch...@omnimn.com> wrote in message
news:69413f9.03032...@posting.google.com...

John Machin

unread,
Mar 24, 2003, 5:28:32 PM3/24/03
to
On 24 Mar 2003 13:31:16 -0800, psch...@omnimn.com (Phil Schmidt)
wrote:

>>> [L[i+1]-L[i] for i in xrange(len(L)-1)]


[3, 3, -5, 6, -8]
>>>

Depends on your definitions of "easily" and "elegantly" :-)

Alex Martelli

unread,
Mar 24, 2003, 5:47:44 PM3/24/03
to
Phil Schmidt wrote:

I see you've already received two excellent suggestions based on
zip, so here's the alternative based on indices instead:

[ L[i+1]-L[i] for i in range(len(L)-1) ]


Alex

Fernando Perez

unread,
Mar 24, 2003, 5:46:51 PM3/24/03
to
Phil Schmidt wrote:

Numeric provides the simplest possible way of writing this:

In [1]: L = array([2, 5, 8, 3, 9, 1])

In [2]: L[1:]-L[:-1]
Out[2]: array([ 3, 3, -5, 6, -8])

Note that in the above, the data is NOT copied when you do L[1:], so even if L
is a multi-megabyte array, you won't incurr a copy penalty. The semantics of
Numeric slicing is different from that of python lists, for very good reasons.
And the loop is done in C, so it's extremely fast.

Cheers,

f.

Greg Ewing (using news.cis.dfn.de)

unread,
Mar 24, 2003, 6:25:08 PM3/24/03
to
Sean Ross wrote:
> This will do what you're looking for:
>
> def adjacentdiffs(seq):
> return [ x - y for x, y in zip (seq[1:], seq[:-1]) ]

While that works, I think it obfuscates what's being
done rather more than necessary.

I would suggest:

[seq[i+1] - seq[i] for i in xrange(len(seq) - 1)]

It's a bit more efficient to boot, since it avoids
constructing an intermediate list of tuples.

--
Greg Ewing, Computer Science Dept,
University of Canterbury,
Christchurch, New Zealand
http://www.cosc.canterbury.ac.nz/~greg

Sean Ross

unread,
Mar 24, 2003, 6:54:27 PM3/24/03
to

"Greg Ewing (using news.cis.dfn.de)" <m...@privacy.net> wrote in message
news:b5o3o8$2bhikr$1...@ID-169208.news.dfncis.de...

> I would suggest:
>
> [seq[i+1] - seq[i] for i in xrange(len(seq) - 1)]
>
> It's a bit more efficient to boot, since it avoids
> constructing an intermediate list of tuples.

True.
And,
import operator
map(operator.sub, seq[1:], seq[:-1])
suffers from the same inefficiency, but it is faster ;)

Sean


Jeremy Fincher

unread,
Mar 24, 2003, 9:00:46 PM3/24/03
to
psch...@omnimn.com (Phil Schmidt) wrote in message news:<69413f9.03032...@posting.google.com>...

> Given a list of numbers, such as:
>
> L = [2, 5, 8, 3, 9, 1]
>
> I want to generate a list containing the differences between adjacent
> elements, i.e.,
>
> Ld = [3, 3, -5, 6, -8]

First, define a "window" iterator (useful for many other things in
addition to this):

def window(L, size):
for i in xrange(len(L) - size + 1):
yield L[i:i+size]

The it's simple:

[y - x for (x, y) in window(L, 2)]

Jeremy

Alex Martelli

unread,
Mar 25, 2003, 6:50:58 AM3/25/03
to
Sean Ross wrote:

No reason not to give timeit.py a try when discussing speed...:

[alex@lancelot alex]$ python timeit.py -s 'import operator' -s
'seq=range(1000)' 'x=map(operator.sub, seq[1:], seq[:-1])'
1000 loops, best of 3: 919 usec per loop
[alex@lancelot alex]$ python timeit.py -s 'import operator' -s
'seq=range(1000)' 'x=[seq[i+1]-seq[i] for i in xrange(len(seq)-1)]'
1000 loops, best of 3: 1.96e+03 usec per loop
[alex@lancelot alex]$ python timeit.py -s 'import operator' -s
'seq=range(1000)' 'x=map(lambda a, b: a-b, seq[1:], seq[:-1])'
1000 loops, best of 3: 1.69e+03 usec per loop
[alex@lancelot alex]$ python timeit.py -s 'import operator' -s
'seq=range(1000)' 'x=[a-b for a,b in zip(seq[1:],seq)]'
100 loops, best of 3: 1.78e+03 usec per loop

so, yes: map is way faster when you can pass it a builtin
function from operator (not so much when you need a lambda,
though). And zip turns out to be a little bit faster (on
this machine) than the xrange-based solution.

One result that surprises me a lot...:

[alex@lancelot alex]$ python timeit.py -s 'import operator' -s
'seq=range(1000)' 'x=map(int.__sub__, seq[1:], seq[:-1])'
1000 loops, best of 3: 1.68e+03 usec per loop

I have no idea how or why operator.sub can be faster
than int.__sub__ in this case...!


Alex

Greg Ewing (using news.cis.dfn.de)

unread,
Mar 25, 2003, 5:58:27 PM3/25/03
to
Alex Martelli wrote:
> I have no idea how or why operator.sub can be faster
> than int.__sub__ in this case...!

Perhaps because int.__sub__ returns a wrapper object
around the underlying function, whereas operator.sub
provides more direct access to it? Not sure about
that.

Michael Hudson

unread,
Mar 26, 2003, 7:38:44 AM3/26/03
to
"Greg Ewing (using news.cis.dfn.de)" <m...@privacy.net> writes:

> Alex Martelli wrote:
> > I have no idea how or why operator.sub can be faster
> > than int.__sub__ in this case...!
>
> Perhaps because int.__sub__ returns a wrapper object
> around the underlying function, whereas operator.sub
> provides more direct access to it? Not sure about
> that.

Yeah, that's what I was thinking too. ceval.c knows how to call
PyCFunction objects directly, but I guess __int__.__sub__ goes through
the whole tp_call thing. The latter may also do more type checking;
not sure about that.

Cheers,
M.

--
Do I do everything in C++ and teach a course in advanced swearing?
-- David Beazley at IPC8, on choosing a language for teaching

Mario A. Botto

unread,
Apr 1, 2003, 6:25:56 PM4/1/03
to
psch...@omnimn.com (Phil Schmidt) wrote in message news:<69413f9.03032...@posting.google.com>...

Hi.
How about this?
If, say, L1 = [a1, a2, a3], L2 = [b1, b2, b3, b4, b5], then
zip(L1, L2) = [(a1,b1), (a2,b2), (a3, b3)]. Hence,
[a - b for (a,b) in zip(L1, L2)] = [a1-b1, a2-b2, a3-b3]

This solves your problem if you set L1 = L, L2 = L[1:]. That is,

Ld = [a - b for (a,b) in zip(L[1:], L)]
print 'Ld: ', Ld
>> Ld: [3, 3, -5, 6, -8]

Cheers.

0 new messages