7

Hi I need to calculate distances between every numbers pair in list, including the distance between the last and the first (it's a circle).

Naively i can do something like that:

l = [10,-12,350]
ret = []
for i in range(len(l)-1):
    ret.append(abs(l[i] - l[i+1]))
ret.append(l[-1] - l[0])
print ret

out: [22, 362, 340]

I tried "enumerate" which is a little bit better way:

print [abs(v - (l+[l[0]])[i+1]) for i, v in enumerate(l)]
out: [22, 362, 340]

Is there more elegant and "pythonic" way?

mclafee
  • 1,406
  • 3
  • 18
  • 25

6 Answers6

5

I'd argue this is a small improvement. There could well be a cleaner way than this though:

print [abs(v - l[(i+1)%len(l)]) for i, v in enumerate(l)]
andy boot
  • 11,355
  • 3
  • 53
  • 66
2

Another method:

print map(lambda x,y: abs(x-y), l[1:] + l[:1], l)
sje397
  • 41,293
  • 8
  • 87
  • 103
1

Not a huge improvement:

>>> [abs(a - b) for a, b in zip(l, l[1:] + l[:-1])]
[22, 362, 340]
icecrime
  • 74,451
  • 13
  • 99
  • 111
0

If you're happy to use numpy...

list(numpy.abs(numpy.ediff1d(l, to_end=l[0]-l[-1])))

This scales well with longer l. Not converting to or from a list will speed things up quite a bit (very often a numpy array can be used in place of a list anyway).

Or you can construct it youself using numpy.roll:

list(numpy.abs(l - numpy.roll(l, -1)))

A few timings:

In [37]: l = list(numpy.random.randn(1000))

In [38]: timeit [abs(v - l[(i+1)%len(l)]) for i, v in enumerate(l)]
1000 loops, best of 3: 936 us per loop

In [39]: timeit list(numpy.abs(numpy.ediff1d(l, to_end=l[0]-l[-1])))
1000 loops, best of 3: 367 us per loop

In [40]: _l = numpy.array(l)

In [41]: timeit numpy.abs(numpy.ediff1d(_l, to_end=l[0]-l[-1]))
10000 loops, best of 3: 48.9 us per loop

In [42]: timeit _l = numpy.array(l); list(numpy.abs(_l - numpy.roll(_l, -1)))
1000 loops, best of 3: 350 us per loop

In [43]: timeit numpy.abs(_l - numpy.roll(_l, -1))
10000 loops, best of 3: 32.2 us per loop

If raw speed is your thing, quicker still, but not so neat, you can use sliced arrays directly:

In [78]: timeit a = numpy.empty(_l.shape, _l.dtype); a[:-1] = _l[:-1] - _l[1:]; a[-1] = _l[-1] - _l[0]; a = numpy.abs(a)
10000 loops, best of 3: 20.5 us per loop
Henry Gomersall
  • 8,434
  • 3
  • 31
  • 54
0

It's probably not as good as other answers in this case, but if used as part of a larger codebase, it could be useful to define an iterator which returns pairs of items over a list, e.g.:

def pairs(l):
    if len(l) < 2:
        return

    for i in range(len(l)-1):
        yield l[i], l[i+1]

    yield l[-1], l[0]

print [abs(a - b) for a,b in pairs([10,-12,350])]

It's not a one-liner, but is fairly readable.

Dave Challis
  • 3,525
  • 2
  • 37
  • 65
0

Combining the answer from icecrime with this answer provides another pythonic possibility:

 print [numpy.linalg.norm(a-b) for a, b in zip(l, l[1:] + l[:-1])]
Community
  • 1
  • 1
Trond Kristiansen
  • 2,379
  • 23
  • 48