Running an operation on each 2 consecutive variables in Python? - python

I have a Python list like:
mylist = [1,2,3,4,5,6,7,8]
And I want to run an operation on each two consecutive variables. For example I want to sum each two consecutive variables in the list and put them into another list:
newlist = [1+2, 3+4, 5+6, 7+8]
But how can I do that in Python? I didn't know where to start. Should I use two nested for loops, or enumerate, or zip function? I am confused.

My favorite way to do this is with an explicit list_iterator.
itr = iter(mylist)
newlist = [x + y for x, y in zip(itr, itr)]
zip advances the iterator by two elements each time it yields a pair. The benefit of using an iterator over slicing is that it doesn't require you to make two half-copies of the list before zipping.
If you don't like two lines, Python 3.8 can fold the assignment into an expression:
from operator import add
newlist = list(map(add, (i:=iter(mylist)), i))
(For some reason, you can't use an assignment expression in a list comprehension.
[x + y for x, y in zip((i:=iter(mylist)), i)]
is a syntax error, but
t = zip((i:=iter(mylist)), i)
[x + y for x, y in t]
is fine. I suspect it has something to do with scoping, but I don't know the technical details.)

Solution with zip():
out = [a+b for a, b in zip(mylist[::2], mylist[1::2])]
print(out)
Prints:
[3, 7, 11, 15]

The range() function defaults to increment the sequence by 1, but one can increment the value by adding a third parameter - range(start, stop, step_size).
You can try this:-
res = [mylist[i]+mylist[i+1] for i in range(0,len(mylist)-1, 2)]
print(res)
Output:-
[3, 7, 11, 15]

You can use this list comprehension:
mylist = [1,2,3,4,5,6,7,8]
mylist =[n+mylist[i-1] for i,n in enumerate(mylist) if i%2]
print(mylist)
Output:
[3, 7, 11, 15]

Related

Using list comprehension to keep items not in second list

I am trying to use list comprehension to remove a number of items from a list by just keeping those not specified.
For example if I have 2 lists a = [1,3,5,7,10] and b = [2,4] I want to keep all items from a that are not at an index corresponding to a number in b.
Now, I tried to use y = [a[x] for x not in b] but this produces a SyntaxError.
y = [a[x] for x in b] works fine and keeps just exact the elements that i want removed.
So how do I achieve this? And on a side note, is this a good way to do it or should I use del?
You can use enumerate() and look up indexes in b:
>>> a = [1, 3, 5, 7, 10]
>>> b = [2, 4]
>>> [item for index, item in enumerate(a) if index not in b]
[1, 3, 7]
Note that to improve the lookup time, better have the b as a set instead of a list. Lookups into sets are O(1) on average while in a list - O(n) where n is the length of the list.
Guess you're looking for somthing like :
[ x for x in a if a.index(x) not in b ]
Or, using filter:
filter(lambda x : a.index(x) not in b , a)
Try this it will work
[j for i,j in enumerate(a) if i not in b ]
after this:
y = [a[x] for x in b]
just add:
for x in y:
a.remove(x)
then you end up with a stripped down list in a

Calling functions on lists

I have a spectra of wavelengths as a list and some number of other lists I use in a formula (using tmm.tmm_core). Is there something more efficient than iterating through the wavelength if I'm just basically doing the same thing for all wavelengths?
Example
def go(n, thk, theta):
#do stuff
return(something)
wv = [1, 2, 3, 4]
a_vec = [3, 7, 3, 9]
b_vec = [6, 5, 9, 3]
c_vec = [0, 1, 8, 9]
theta = 0
th = [10, 1, 10]
final = []
for i in range(len(wv)):
n = [a[i], b[i], c[i]]
answer = go(n, th, theta)
final.append(answer)
in reality there are maybe 5000-10000 rows. It just seems to lag a bit when I press go and I assume it's because of the iteration. Pretty new to optimizing so I haven't used any benchmarking tools or anything.
I think you're looking for the map function in Python!
>>> list1 = [1,2,3,4]
>>> list2 = [5,6,7,8]
>>> map(lambda x,y: x+y, list1, list2)
[6, 8, 10, 12]
it takes in a function (in the above case, an anonymous lambda function), one or more lists and returns another list. At each iteration within the function, both lists are iterated and the result is added to the new list. You don't need to limit yourself to the expressive power of a lambda statement; you can also use globally defined functions as in the case below:
>>> def go(a,b,c):
... return a+b+c
...
>>> map(go, list1,list2, range(9,13))
[15, 18, 21, 24]
You can put all of your lists within a custom list like C_list and use map to create a new list all_len contain the length of all lists then use a list comprehension to create the list final :
all_len=map(len,C_list)
final =[[go([a[i], b[i], c[i]], th, theta) for i in range(li)] for li in all_len]
Also if the length of a and b and c are equal you can use zip function to zip then and refuse of multiple indexing :
all_len=map(len,C_list)
z=zip(a,b,c)
final =[[go(z[i], th, theta) for i in range(li)] for li in all_len]
If you have to perform an operation on every item in the list, then you're gonna have to go through every item in the list. However, you could gain speed through the use of list comprehensions: List Comprehensions

Combining two lists

I am trying to merge 2 lists like so
coordinates_X = [1, 17, 9]
coordinates_Y = [3, 5, 24]
outcome = [1, 3, 17, 5, 9, 24]
Are the lists always the same length? If so, this will give you a list of tuples:
outcome = zip(coordinates_X, coordinates_Y)
You can then flatten that:
import itertools
outcome = list(itertools.chain.from_iterable(zip(coordinates_X, coordinates_Y)))
For 2.x, itertools also has izip available, which builds an iterable yielding tuples. But that's unnecessary for lists this small. On 3.x, zip always returns an iterable.
If they're not the same length, zip or itertools.izip will truncate the outcome to match the shorter list. itertools.izip_longest can extend a shorter list with a fill value specified in the call, if you need that.
An alternate without itertools:
result = []
for i in zip(coordinates_X,coordinates_Y):
result.extend(i)
** the Code you can use is: **
coordinates_X = [1, 17, 9]
coordinates_Y = [3, 5, 24]
outcome =coordinates_X+coordinates_Y
If ordering isn't required, you can do:
coordinates_X + coordinates_Y
Also, you can use list comprehensions to output exactly what you
[ x for y in map(lambda x, y: [x, y], coordinates_X, coordinates_Y) for x in y ]
Probably not the best way to do it, but it was what occurred to me :).

How to refer to next element in a loop?

I've been looking around for this but can't find what I would like to. I'm sure I've seen this done before but I can't seem to find it. Here's an example:
In this case I would like to take the difference of each element in an array,
#Generate sample list
B = [a**2 for a in range(10)]
#Take difference of each element
C = [(b+1)-b for b in B]
the (b+1) is to denote the next element in the array which I don't know how to do and obviously doesn't work, giving the result:
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
the result I would like is:
[1, 3, 5, 7, 9, 11, 13, 15, 17]
I understand that this result is shorter than the original array however the reason for this would be to replace ugly expressions such as:
C = [B[i+1]-B[i] for i in range(len(B)-1)]
In this case it really isn't that bad at all, but there are cases that I need to iterate through multiple variables with long expressions and it gets annoying to keep having to write the index in each time. Right now I'm hoping that there is an easy pythonic way to do this that I don't know about...
EDIT: An example of what I mean about having to do this with multiple variables would be:
X = [a for a in range(10)]
Y = [a**2 for a in range(10)]
Z = [a**3 for a in range(10)]
for x,y,z in zip(X,Y,Z):
x + (x+1) + (y-(y+1))/(z-(z+1))
where (x+1),(y+1),(z+1) denote the next element rather than:
for i in range(len(X)):
x[i] + x[i+1] + (y[i]-y[i+1])/(z[i]-z[i+1])
I am using python 2.7.5 btw
re: your edit
zip is still the right solution. You just need to zip together two iterators over the lists, the second of which should be advanced one tick.
from itertools import izip,tee
cur,nxt = tee(izip(X,Y,Z))
next(nxt,None) #advance nxt iterator
for (x1,y1,z1),(x2,y2,z2) in izip(cur,nxt):
print x1 + x2 + (y1-y2)/(z1-z2)
If you don't like the inline next call, you can use islice like #FelixKling mentioned: izip(cur,islice(nxt, 1, None)).
Alternative you can use zip, to create tuples of the current value, next value:
C = [b - a for a, b in zip(B, B[1:])]
I believe zip returns a generator in Python 3. In Python 2, you might want to use izip. And B[1:], you could use islice: islice(B, 1, None).
Maybe you want enumerate. As follows:
C = [B[b+1]-item for b,item in enumerate(B[:-1])]
or simply:
C = [B[b+1]-B[b] for b in range(len(B[:-1]))]
They both work.
Examples
>>> B = [a**2 for a in range(10)]
>>> C = [B[b+1]-item for b,item in enumerate(B[:-1])]
>>> print C
[1, 3, 5, 7, 9, 11, 13, 15, 17]
This is a pretty weird way, but it works!
b = [a**2 for a in range(10)]
print b
print reduce(lambda x, y:len(x) and x[:-1]+[y-x[-1], y] or [y], b, [])
I have created a bunk on CodeBunk so you can run the it too
http://codebunk.com/b/-JJzLIA-KZgASR_3a-I8

Sum nested lists in Python

I have a nested list in python, i=[[1,2,3],[4,5,6]]. I want to sum the terms such that the final result is j=[1+4,2+5,6+3]. I have tried:
i=[[1,2,3],[4,5,6]]
j=[sum(x) for x in zip(i)]
But this is what I get instead:
>>>print j
[6, 15]
zip does not take a list of lists as an argument. It takes an arbitrary long list of list arguments.
Here is how to do it:
i=[[1,2,3],[4,5,6]]
j=[sum(x) for x in zip(*i)]
You forgot the *
>>> i=[[1,2,3],[4,5,6]]
>>> [sum(x) for x in zip(*i)]
[5, 7, 9]

Categories