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.
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.
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.
[abs(j-A[i+1]) for i,j in enumerate(A[:-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]
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'
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
Related
My issue is as follows: I want to create a program that accepts a random list (l) from the list. This randomly chosen list, in its turn, contains other lists. Then it should find the shortest list inside l and cut all other lists inside l by the length of the shortest one. These lists should be appended to a new list (l_exit) as follows: [l[1][1], l[2][1], ... , l[i][1], l[1][2], ... , l[i][k]]. Then all elements, which have been cut, should be added to the end of l_exit.
Input: [[1,2,3],['a', 'b'], [30, 40, 50, 60]]
Output: [1, 'a', 30, 2, 'b', 40], [3, 50, 60]
(shortest length is 2 (['a','b']). we cut every list by 2 and
therefore have new following lists [1,2],['a','b'],[30,40]. They are
added accordingly to l_exit[0] (firstly go first elements of each
list, secondly - second and so on). In the end we add a new list of
items that have been left, that have been cut from pre-existing lists: 3,50,60)
When I try running the program with above-mentioned input, I have the following output:
changed l: [[1, 2, 3], ['a', 'b'], [30, 40, 50, 60]]
l_exit: [[40, 'b', 2, 30, 'a', 1, 1],[]]
What can be the source of the problem? I will appreciate any help! Thanks in advance!
The program:
import random
l_1 = [[1,2,3],['a', 'b'], [30, 40, 50, 60]]
l_2 = [[1,2,3,4,5],['a', 'b','c'], [30, 40, 50, 120]]
l_3 = [[1],['a', 'b'], [30, 40, 50, 60, 101, 120]]
l_rand = [l_1,l_2,l_3]
l = random.choice(l_rand)
l_exit = [[],[]]
if (len(l[1]) < len(l[2])):
index = len(l[1])
else:
index = len(l[2])
for i in range (len(l)):
if (len(l[i]) < index):
l_exit[1] = l[i][index:]
l[i] = l[i][:index]
for i in range (index):
for k in range(len(l)):
l_exit.insert(0,(l[k][i]))
print('changed l:', l)
print('l_exit: ', l_exit)
You can use the zip function to get your first part. First we 'unpack' using the *, then we loop over the lists:
x = [[1,2,3],['a', 'b'], [30, 40, 50, 60]]
out = []
for i in zip(*x):
out.extend([*i])
out
#[1, 'a', 30, 2, 'b', 40]
zip finishes at the shortest element, so we are done.
To get the remainder list, we can do a little extra work to figure out where we finished, using a counter, and then subset the original lists:
out = []
left = []
count = 0
for i in zip(*x):
out.extend([*i])
count += 1
for i in x:
left.extend(i[count:])
out, left
#([1, 'a', 30, 2, 'b', 40], [3, 50, 60])
Defining index
Firstly, there is an issue in the following if/else tree:
if (len(l[1]) < len(l[2])):
index = len(l[1])
else:
index = len(l[2])
What happens in the case that l[0] is the shortest list within l? Or cases in which the longest list within l has an index >2?
If index is to be defined as "An integer representing the len of the shortest list stored within l" then I would suggest the following:
index = len(sorted(l, key=len)[0])
The code is quite concise and basically runs as "The value of index is the len of the first list stored in a copy of l that has been sorted by len". This will stop unexpected/undesirable behaviour that may be generated by your original index definition as discussed above.
Populating l_exit
As far as I understand, l_exit should be a list containing:
A list containing all values up to (but not including as to account for index from 0) the index of index from each list within l - stored in l_exit[0]
A list containing all values from the index of index from each list within l - stored in l_exit[1]
First you try to populate l_exit[1] using the following loop:
for i in range (len(l)):
if (len(l[i]) < index):
l_exit[1] = l[i][index:]
l[i] = l[i][:index]
The problem here is that given index refers to a value that is the shortest len of an element in the top-level of the list l (as discussed previously this may not be reliable in the current implementation but would be in the example output you provided), the code held in the if len(l[i]) < index): block will never execute - since by definition no len(l[i]) for i in l could be < index.
Conclusion
I won't go into further detail on issues within your code here as I feel it is obvious that you should spend some time reviewing each statement/structure, debugging with a debugger or console printouts as best fits your attitude - could you state what each line of your code is doing if asked?
I would however recommend looking into the built-in enumerate() method - enumerate docs - as I feel that you might be able to use this to good effect as to improve the readability/maintainability of your code.
Solution
I think it would be time well spent to review your existing code yourself, but for what it's worth here's my full solution to the problem posed.
import random
l_1 = [[1,2,3],['a', 'b'], [30, 40, 50, 60]]
l_2 = [[1,2,3,4,5],['a', 'b','c'], [30, 40, 50, 120]]
l_3 = [[1],['a', 'b'], [30, 40, 50, 60, 101, 120]]
l_rand = [l_1,l_2,l_3]
l = random.choice(l_rand)
index = len(sorted(l, key=len)[0])
l_exit = [[j for i in l for j in i[:index]], [y for x in l for y in x[index:]]]
print(f'l: {l}')
print(f'l_exit: {l_exit}')
Test Output
l: [[1, 2, 3], ['a', 'b'], [30, 40, 50, 60]]
l_exit: [[1, 2, 'a', 'b', 30, 40], [3, 50, 60]]
Let's say I have a python list:
[4,5,25,60,19,2]
How can I add every nth entry to each other?
e.g. I split the list into 3 entries [ 4,5 / 25,60 / 19,2 ], then add these entries in order to get a new list:
[4+25+19, 5+60+2]
Which gives me the sum:
[48, 67]
For a more complex example, lets say I have 2000 entries in my list. I want to add every 100th entry to the one before so I get 100 entries in the new list. Each entry would now be the sum of every 100th entry.
Iteratively extract your slices and sum them up.
>>> [sum(l[i::2]) for i in range(len(l) // 3)]
[48, 67]
You may have to do a bit more to handle corner cases but this should be a good start for you.
The itertools documentation has a recipe function called grouper, you can import it from more_itertools (needs manual install) or copy paste it.
It works like this:
>>> from more_itertools import grouper
>>> l = [4,5,25,60,19,2]
>>> list(grouper(2, l)) # 2 = len(l)/3
>>> [(4, 5), (25, 60), (19, 2)]
You can transpose the output of grouper with zip and apply sum to each group.
>>> [sum(g) for g in zip(*grouper(2, l))]
>>> [48, 67]
I prefer this to manually fiddling with indices. In addition, it works with any iterable, not just lists. A generic iterable may not support indexing or slicing, but it will always be able to produce a stream of values.
Using the chunks function taken from here, you could write the following:
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]
l = [4,5,25,60,19,2]
print([sum(e) for e in list(chunks(l, 2))])
There may be a smart sequence of list operations that you could use but I couldn't think of any. So instead I just did a parser that goes from 0 to n-1 and within the confines of the list adds the elements, going every n. So if n=3, you go 0, 3, 6, etc; then 1, 4, 7, etc. - and put it into the output list.
The code is attached below. Hope it helps.
list1 = [7, 6, -5.4, 6, -4, 55, -21, 45, 67, -9, -8, -7, 8, 9, 11, 110, -0.8, -9.8, 1.1]
n = 5
list2 = []
sum_elem = 0
for i in range(n):
sum_elem = 0
j = i
while j < len( list1 ):
sum_elem += list1[j]
j += n
list2.append(sum_elem)
print( list2 )
Say I have a simple list of numbers, e.g.
simple_list = range(100)
I would like to shorten this list such that the gaps between the values are greater than or equal to 5 for example, so it should look like
[0, 5, 10...]
FYI the actual list does not have regular increments but it is ordered
I'm trying to use list comprehension to do it but the below obviously returns an empty list:
simple_list2 = [x for x in simple_list if x-simple_list[max(0,x-1)] >= 5]
I could do it in a loop by appending to a list if the condition is met but I'm wondering specifically if there is a way to do it using list comprehension?
This is not a use case for a comprehension, you have to use a loop as there could be any amount of elements together that have less than five between them, you cannot just check the next or any n amount of numbers unless you knew the data had some very specific format:
simple_list = range(100)
def f(l):
it = iter(l)
i = next(it)
for ele in it:
if abs(ele - i) >= 5:
yield i
i = ele
yield i
simple_list[:] = f(simple_list)
print(simple_list)
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
A better example to use would be:
l = [1, 2, 2, 2, 3, 3, 3, 10, 12, 13, 13, 18, 24]
l[:] = f(l)
print(l)
Which would return:
[1, 10, 18, 24]
If your data is always in ascending order you can remove the abs and just if ele - i >= 5.
If I understand your question correctly, which I'm not sure I do (please clarify), you can do this easily. Assume that a is the list you want to process.
[v for i,v in enumerate(a) if abs(a[i] - a[i - 1]) >= 5]
This gives all elements with which the difference to the previous one (should it be next?) are greater or equal than 5. There are some variations of this, according to what you need. Should the first element not be compared and excluded? The previous implementation compares it with index -1 and includes it if the criteria is met, this one excludes it from the result:
[v for i,v in enumerate(a) if i != 0 and abs(a[i] - a[i - 1]) >= 5]
On the other hand, should it always be included? Then use this:
[v for i,v in enumerate(a) if (i != 0 and abs(a[i] - a[i - 1]) >= 5) or (i == 0)]
Given an list of integers does exists a default method find the max distance between values?
So if I have this array
[1, 3, 5, 9, 15, 30]
The max step between the values is 15. Does the list object has a method for do that?
No, list objects have no standard "adjacent differences" method or the like. However, using the pairwise function mentioned in the itertools recipes:
def pairwise(iterable):
a, b = tee(iterable)
next(b, None)
return izip(a, b)
...you can (concisely and efficiently) define
>>> max(b-a for (a,b) in pairwise([1, 3, 5, 9, 15, 30]))
15
No, but it's trivial to code:
last = data[0]
dist = 0
for i in data[1:]:
dist = max(dist, i-last)
last = i
return dist
You can do:
>>> s = [1, 3, 5, 9, 15, 30]
>>> max(x[0] - x[1] for x in zip(s[1:], s))
15
This uses max and zip. It computes the difference between all consecutive elements and returns the max of those.
l=[1, 3, 5, 9, 15, 30]
max([j-i for i, j in zip(l[:-1], l[1:])])
That is using pure python and gives you the desired output "15".
If you like to work with "numpy" you could do:
import numpy as np
max(np.diff(l))
The list object does not. However, it is pretty quick to write a function that does that:
def max_step(my_list):
max_step = 0
for ind in xrange(len(my_list)-1):
step = my_list[ind+1] - my_list[ind]
if step > max_step:
max_step = step
return max_step
>>> max_step([1, 3, 5, 9, 15, 30])
15
Or if you prefer even shorter:
max_step = lambda l: max([l[i+1] - l[i] for i in xrange(len(l)-1)])
>>> max_step([1, 3, 5, 9, 15, 30])
15
It is possible to use the reduce() function, but it is not that elegant as you need some way to keep track of the previous value:
def step(maxStep, cur):
if isinstance(maxStep, int):
maxStep = (abs(maxStep-cur), cur)
return (max(maxStep[0], abs(maxStep[1]-cur)), cur)
l = [1, 3, 5, 9, 15, 30]
print reduce(step, l)[0]
The solution works by returing the previous value and the accumulated max calculation as a tuple for each iteration.
Also what is the expected outcome for [10,20,30,5]? Is it 10 or 25? If 25 then you need to add abs() to your calculation.
I am fairly new to python and am trying to figure out how to duplicate items within a list. I have tried several different things and searched for the answer extensively but always come up with an answer of how to remove duplicate items, and I feel like I am missing something that should be fairly apparent.
I want a list of items to duplicate such as if the list was [1, 4, 7, 10] to be [1, 1, 4, 4, 7, 7, 10, 10]
I know that
list = range(5)
for i in range(len(list)):
list.insert(i+i, i)
print list
will return [0, 0, 1, 1, 2, 2, 3, 3, 4, 4] but this does not work if the items are not in order.
To provide more context I am working with audio as a list, attempting to make the audio slower.
I am working with:
def slower():
left = Audio.getLeft()
right = Audio.getRight()
for i in range(len(left)):
left.insert(????)
right.insert(????)
Where "left" returns a list of items that are the "sounds" in the left headphone and "right" is a list of items that are sounds in the right headphone. Any help would be appreciated. Thanks.
Here is a simple way:
def slower(audio):
return [audio[i//2] for i in range(0,len(audio)*2)]
Something like this works:
>>> list = [1, 32, -45, 12]
>>> for i in range(len(list)):
... list.insert(2*i+1, list[2*i])
...
>>> list
[1, 1, 32, 32, -45, -45, 12, 12]
A few notes:
Don't use list as a variable name.
It's probably cleaner to flatten the list zipped with itself.
e.g.
>>> zip(list,list)
[(1, 1), (-1, -1), (32, 32), (42, 42)]
>>> [x for y in zip(list, list) for x in y]
[1, 1, -1, -1, 32, 32, 42, 42]
Or, you can do this whole thing lazily with itertools:
from itertools import izip, chain
for item in chain.from_iterable(izip(list, list)):
print item
I actually like this method best of all. When I look at the code, it is the one that I immediately know what it is doing (although others may have different opinions on that).
I suppose while I'm at it, I'll just point out that we can do the same thing as above with a generator function:
def multiply_elements(iterable, ntimes=2):
for item in iterable:
for _ in xrange(ntimes):
yield item
And lets face it -- Generators are just a lot of fun. :-)
listOld = [1,4,7,10]
listNew = []
for element in listOld:
listNew.extend([element,element])
This might not be the fastest way but it is pretty compact
a = range(5)
list(reduce(operator.add, zip(a,a)))
a then contains
[0, 0, 1, 1, 2, 2, 3, 3, 4, 4]
a = [0,1,2,3]
list(reduce(lambda x,y: x + y, zip(a,a))) #=> [0,0,1,1,2,2,3,3]