8

Given a list of numbers, I am trying to write a code that finds the difference between consecutive elements. For instance, A = [1, 10, 100, 50, 40] so the output of the function should be [0, 9, 90, 50, 10]. Here is what I have so far trying to use recursion:

def deviation(A):
    if len(A) < 2:
        return
    else:
        return [abs(A[0]-A[1])] + [deviation(A[1: ])]

The output I get, however, (using the above example of A as the input) is [9, [90, [50, [10, None]]]]. How do I properly format my brackets? (I've tried guessing and checking but I this is the closest I have gotten) And how do I write this where it subtracts the current element from the previous element without getting an index error for the first element? I still want the first element of the output list to be zero but I do not know how to go about this using recursion and for some reason that seems the best route to me.

freakish
  • 54,167
  • 9
  • 132
  • 169
user3758443
  • 149
  • 1
  • 3
  • 10

6 Answers6

13

You can do:

[y-x for x, y in zip(A[:-1], A[1:])] 


>>> A = [1, 10, 100, 50, 40]
>>> [y-x for x, y in zip(A[:-1], A[1:])]
[9, 90, -50, -10]

Note that the difference will be negative if the right side is smaller, you can easily fix this (If you consider this wrong), I'll leave the solution for you.

Explanation:

The best explanation you can get is simply printing each part of the list comprehension.

  • A[:-1] returns the list without the last element: [1, 10, 100, 50]
  • A[1:] returns the list without the first element: [10, 100, 50, 40]
  • zip(A[:-1], A[1:]) returns [(1, 10), (10, 100), (100, 50), (50, 40)]
  • The last step is simply returning the difference in each tuple.
Maroun
  • 94,125
  • 30
  • 188
  • 241
7

The simplest (laziest) solution is to use the numpy function diff:

>>> A = [1, 10, 100, 50, 40]
>>> np.diff(A)
array([  9,  90, -50, -10])

If you want the absolute value of the differences (as you've implied by your question), then take the absolute value of the array.

FuzzyDuck
  • 1,492
  • 12
  • 14
2
[abs(j-A[i+1]) for i,j in enumerate(A[:-1])]
Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • @dawg, I had use itertools pairwise first but I changed it before the delete vote so I still cannot understand where the delete vote came from. But on a large list pairwise performs on a par so either way it makes no sense. – Padraic Cunningham Jul 07 '14 at 16:09
  • another downvote, there are some very sad people on here – Padraic Cunningham Jul 07 '14 at 16:43
1

Actually recursion is an overkill:

def deviation(A):
    yield 0
    for i in range(len(A) - 1):
        yield abs(A[i+1] - A[i])

Example:

>>> A = [3, 5, 2]
>>> list(deviation(A))
[0, 2, 3]

EDIT: Yet, another, even simplier and more efficient solution would be this:

def deviation(A):
    prev = A[0]
    for el in A:
        yield abs(el - prev)
        prev = el
freakish
  • 54,167
  • 9
  • 132
  • 169
  • Iterating a range in order to index an iterable is rarely appropriate. Better to use [tee](https://docs.python.org/2/library/itertools.html#itertools.tee) if taking this approach. – wim Jul 07 '14 at 15:55
  • @wim The code is in Python3+. Use `xrange` in Python<3. Other then that there is nothing inappropriate in using `range`: it is simple and efficient. Actually `tee` is inappropriate, inefficient, complicated and a proper overkill. – freakish Jul 07 '14 at 16:23
  • The inefficiency is not just in iterating the range, it's in calling `__getitem__` _twice_ for each element. The list is itself iterable, so there is no need to iterate a range and use `list.__getitem__` at all, it's better to use `list.__iter__` in the first place. This goes for python2 and python3. – wim Jul 07 '14 at 16:36
  • @wim First of all `__getitem__` is implemented on C level and it is O(1) since we are dealing with dynamic arrays. It is very efficient. Iterating over the list has exactly the same complexity. Secondly look at the source of `tee`: it's doing way more complicated things, appending, poping, etc. etc. All in all it is way less efficient. – freakish Jul 07 '14 at 17:28
  • Can you backup your claims with evidence please, then I will remove my downvote. I was not able to make your solution more efficient than an approach using `tee`, even after I removed the extra overhead of converting the generator into a list. My numbers are here: http://ideone.com/luWFKN – wim Jul 07 '14 at 19:07
  • @wim I can't, I stand corrected. The efficiency is pretty much the same in my tests (with your `wim` function slightly faster). I would probably used `tee` if efficiency was cructial, otherwise I would stick with `range` since the solution is easier to understand. Anyway I'm not sure why did you downvote it? Does it not answer the question? Feel free to post your answer as well. – freakish Jul 07 '14 at 23:04
  • @wim I've also added even simplier solution. – freakish Jul 07 '14 at 23:13
  • yes, your new solution is superior. it is more efficient because it's now iterating the iterable directly instead of iterating a range to index the iterable. – wim Jul 08 '14 at 09:57
1

You can do a list comprehension:

>>> A = [1, 10, 100, 50, 40]
>>> l=[A[0]]+A
>>> [abs(l[i-1]-l[i]) for i in range(1,len(l))]
[0, 9, 90, 50, 10]
dawg
  • 98,345
  • 23
  • 131
  • 206
1

For a longer recursive solution more in line with your original approach:

def deviation(A) :
    if len(A) < 2 :
        return []
    else :
        return [abs(A[0]-A[1])] + deviation(A[1:])

Your bracket issue is with your recursive call. Since you have your [deviation(a[1: ])] in its own [] brackets, with every recursive call you're going to be creating a new list, resulting in your many lists within lists.

In order to fix the None issue, just change your base case to an empty list []. Now your function will add 'nothing' to the end of your recursively made list, as opposed to the inherent None that comes with a blank return'