pythonic way of iterating over variable number of sequences [duplicate] - python

Say I have list1 = [1,2,3,4] and list2 = [5,6,7,8]. How would I compare the first element, 1, in list1 with the first element, 5, in list2? And 2 with 6, 3 with 7, and so on.
I'm trying to use a for loop for this, but I'm not sure how to do it. I understand that doing for x in list1 just checks an element x to all elements in list1, but I don't know how to do it when comparing two lists the way I described.

You can traverse both lists simultaneously using zip:
for (x, y) in zip(list1, list2): do_something
The 'zip' function gives you [(1,5), (2,6), (3,7), (4,8)], so in loop iteration N you get the Nth element of each list.

The default comparison operators compare lists in lexicographical order. So you can say things like:
>>> [1, 2, 3, 4] < [5, 6, 7, 8]
True
If instead you want to compute the elementwise comparison, you can use map and cmp (or any other operator:
>>> map(cmp, [1, 2, 3, 4], [5, 6, 7, 8])
[-1, -1, -1, -1]

If your result is going to be a new list, then you can use a list comprehension:
new_list = [ some_function(i, j) for i, j in zip(list1, list2) ]
Here's a real example of the above code:
>>> list1 = [1, 2, 3, 4]
>>> list2 = [1, 3, 4, 4]
>>> like_nums = [ i == j for i, j in zip(list1, list2) ]
>>> print like_nums
[True, False, False, True]
This will make a list of bools that show whether items of the same index in two lists are equal to each other.
Furthermore, if you use the zip function, there is a way to unzip the result when you are done operating on it. Here's how:
>>> list1 = [1, 2, 3, 4]
>>> list2 = [1, 3, 4, 4]
>>> new_list = zip(list1, list2) # zip
>>> print new_list
[(1, 1), (2, 3), (3, 4), (4, 4)]
>>> newlist1, newlist2 = zip(*new_list) # unzip
>>> print list(newlist1)
[1, 2, 3, 4]
>>> print list(newlist2)
[1, 3, 4, 5]
This could be useful if you need to modify the original lists, while also comparing the elements of the same index in some way.

Related

Python | Insert Two Items in a List per Iteration with One-Liner

Is it possible to insert in a list 2 items with one-liner?
For example, get [1, 2, 3, 4] by something like [ x, x+1 for x in [1, 3]]
No you can't do this. Instead you can use a generator expression within itertools.chain.from_iterable in order to chain the iterable items or use a nested list comprehension (which is not as optimized as chain.from_iterable since you have to create the items then unpack them with another loop).
>>> from itertools import chain
>>> l = [(1, 2), (3, 5)]
>>>
>>> list(chain.from_iterable(i for i in l))
[1, 2, 3, 5]
In python 3.5+ you can unpack the iterables within a list like following but still not at iteration time.
>>> a = (1, 2)
>>> b = (3, 5)
>>>
>>> [*a, *b]
[1, 2, 3, 5]
[item for sublist in [ [x, x+1] for x in [1, 3] ] for item in sublist]

function of difference between value

Is there a function in Python to get the difference between two or more values in a list? So, in those two lists:
list1 = [1, 5, 3, 7]
list2 = [4, 2, 6, 4]
I need to calculate the difference between every value in list1 and list2.
for i in list1:
for ii in list2:
print i -ii
This gives negative values, but I want the subtraction between the values of the two lists only from highest value to lowest value for not getting negative values.
For the above lists, I expect the output to be [3, 3, 3, 3].
Thanks.
Assuming you expect [3, 3, 3, 3] as the answer in your question, you can use abs and zip:
[abs(i-j) for i,j in zip(list1, list2)]
Either zip the lists, or use numpy:
>>> list1 = [1, 5, 3, 7]
>>> list2 = [4, 2, 6, 4]
>>> [a-b for a,b in zip(list1, list2)]
[-3, 3, -3, 3]
>>> import numpy as np
>>> np.array(list1) - np.array(list2)
array([-3, 3, -3, 3])
Remember to cast the array back to a list as needed.
edit:
In response to the new requirement that the absolute values are needed: you can add abs in the list comprehension:
>>> [abs(a-b) for a,b in zip(list1, list2)]
[3, 3, 3, 3]
and the numpy solution would change to:
>>> map(abs, np.array(list1) - np.array(list2))
[3, 3, 3, 3]
You may also do if else condition inside list comprehension.
>>> [i-j if i>j else j-i for i,j in zip(list1, list2)]
[3, 3, 3, 3]
You can use zip method in order combine these two lists. See the tutorials for zip method https://docs.python.org/2/library/functions.html#zip
>>> list1 = [1, 5, 3, 7]
>>> list2 = [4, 2, 6, 4]
>>> [abs(x-y) for x, y in zip(list1, list2)]
[3, 3, 3, 3]
Avinash Raj's answer is correct, or alternatively, using map():
from operator import sub
C = map(sub, A, B)

Repeat each item in a list a number of times specified in another list

I have two lists, x and y:
>>> x = [2, 3, 4]
>>> y = [1, 2, 3]
I want to use these to create a new list. The new list will have each element in x repeated the number of times specified by the corresponding element in y. Hence, the desired output is
>>> new_list
[2, 3, 3, 4, 4, 4]
The order of the elements in new_list doesn't matter to me. It's also not crucial that it be a list -- any sequence type is fine.
What is the fastest, most efficient, most Pythonic way to achieve this?
numpy's repeat function gets the job done:
>>> import numpy as np
>>> x = [2, 3, 4]
>>> y = [1, 2, 3]
>>> np.repeat(x, y)
array([2, 3, 3, 4, 4, 4])
You can use list comprehension, like this
>>> x = [2, 3, 4]
>>> y = [1, 2, 3]
>>> [item for item, count in zip(x, y) for i in range(count)]
[2, 3, 3, 4, 4, 4]
Here, we zip the x and y so that the element from x and its corresponding count from y are grouped as a single tuple. Then, we iterate count number of items to produce the same item.
If your objects in x are immutables, then you can create count copies of the same and put them together in a list, like this
>>> [i for item, count in zip(x, y) for i in [item] * count]
[2, 3, 3, 4, 4, 4]
You can do the same lazily, with itertools.repeat, like this
>>> from itertools import chain, repeat
>>> chain.from_iterable((repeat(item, count) for item, count in zip(x,y)))
<itertools.chain object at 0x7fabe40b5320>
>>> list(chain.from_iterable((repeat(item, cnt) for item, cnt in zip(x,y))))
[2, 3, 3, 4, 4, 4]
Please note that the chain returns an iterable, not a list. So, if you don't want all the elements at once, you can get the items one by one from it. This will be highly memory efficient if the count is going to be a very big number, as we don't create the entire list in the memory immediately. We generate the values on-demand.
Thanks ShadowRanger. You can actually apply repeat over x and y and get the result like this
>>> list(chain.from_iterable(map(repeat, x, y)))
[2, 3, 3, 4, 4, 4]
here, map function will apply the values from x and y to repeat one by one. So, the result of map will be
>>> list(map(repeat, x, y))
[repeat(2, 1), repeat(3, 2), repeat(4, 3)]
Now, we use chain.from_iterable to consume values from each and every iterable from the iterable returned by map.
Simple using for loop.
>>> x = [2, 3, 4]
>>> y = [1, 2, 3]
>>> final = []
>>> for index, item in enumerate(y):
final.extend([x[index]]*item)
One way to achieve this is via using .elements() function of collections.Counter() along with zip. For example:
>>> from collections import Counter
>>> x = [2, 3, 4]
>>> y = [1, 2, 3]
# `.elements()` returns an object of `itertool.chain` type, which is an iterator.
# in order to display it's content, here type-casting it to `list`
>>> list(Counter(dict(zip(x,y))).elements())
[2, 3, 3, 4, 4, 4]

Deleting duplicates in list of lists in Python 2.7

Trenutno_stanje is list
povijest is list of lists
epsilon_okolina is function that gives list for a string (pocetno):
trenutno_stanje.append(pocetno)
trenutno_stanje.extend(epsilon_okolina[pocetno])
povijest.append(trenutno_stanje)
povijest should be essentialy list of lists, but it somehow in code duplicates entities
in a way it can be avoided.
What I would like to know is how to remove duplicate of strings in lists of a list?
I tried:
for p in povijest:
p=list(set(p))
But it changed nothing
In your for loop you are just reassigning p and not actually changing the povijest list. Also, set only works on hashable types, and list certainly is not one. You want to use list comprehension after you cast the lists inside the main list into something that can be hashed (like a tuple, which is an immutable list) and then turn that into a set.
>>> a = [1, 2, 3, 4]
>>> b = [1, 2, 3, 4]
>>> c = [1, 3, 4]
>>> i1 = [a, b, c]
>>> set([tuple(x) for x in i1])
set([(1, 2, 3, 4), (1, 3, 4)])
>>> b = [[1,2,3, 3], [3, 2, 4,4]]
>>> b = [ list(set(x)) for x in b ]
>>> b
[[1, 2, 3], [2, 3, 4]]
If you curious about the order try this ,
>>> a=[1,2,3,4,5]
>>> b=[2,3,1,2]
>>> c=[1,2,3,4,5]
>>> z=[a,b,c]
>>> dict((x[0], x) for x in z).values()
[[1, 2, 3, 4, 5], [2, 3, 1, 2]]

array except other

I have 2 arrays:
arr1 = [a,b,c,d,e]
arr2 = [c,d,e]
I want to give array arr1 except arr2.
Mathematically, you're looking for a difference between two sets represented in lists. So how about using the Python set, which has a builtin difference operation (overloaded on the - operator)?
>>>
>>> arr = [1, 2, 3, 4, 5]
>>> arr2 = [3, 4, 9]
>>> set(arr) - set(arr2)
>>> sdiff = set(arr) - set(arr2)
>>> sdiff
set([1, 2, 5])
>>> list(sdiff)
[1, 2, 5]
>>>
It would be more convenient to have your information in a set in the first place, though. This operation suggests that a set better fits your application semantics than a list. On the other hand, if you may have duplicates in the lists, then set is not a good solution.
So you want the difference of two lists:
list1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list2 = [1, 2, 3, 4, 4, 6, 7, 8, 11, 77]
def list_difference(list1, list2):
"""uses list1 as the reference, returns list of items not in list2"""
diff_list = []
for item in list1:
if not item in list2:
diff_list.append(item)
return diff_list
print list_difference(list1, list2) # [5, 9, 10]
Or using list comprehension:
# simpler using list comprehension
diff_list = [item for item in list1 if item not in list2]
print diff_list # [5, 9, 10]
If you care about (1) preserving the order in which the items appear and (2) efficiency in the case where your lists are large, you probably want a hybrid of the two solutions already proposed.
list2_items = set(list2)
[x for x in list1 if x not in list2_items]
(Converting both to sets will lose the ordering. Using if x not in list2 in your list comprehension will give you in effect an iteration over both lists, which will be inefficient if list2 is large.)
If you know that list2 is not very long and don't need to save every possible microsecond, you should probably go with the simple list comprehension proposed by Flavius: it's short, simple and says exactly what you mean.

Categories

Resources