How does exact list comparison work? - python

I was making a code in which I needed to compare two lists for exact matches, and I found this code (I had to add print so it would output the result):
a = [1,2,3,4,5]
b = [9,8,7,6,5]
print [i for i, j in zip(a, b) if i == j]
This code outputs [5] because it prints the list, and if I change the code to
a = [1,2,3,5,4]
b = [9,8,7,6,5]
print [i for i, j in zip(a, b) if i == j]
It outputs [] because the list is empty.
This is all fine and good because it solves my list comparison problem, but I have almost no idea why or how it works. I would greatly appreciate either a detailed or partial explanation if you have one.

That's called a "list comprehension", let's break it down here:
[i for i, j in zip(a, b) if i == j] can be roughly understood as making a list of [i], where i (and j) come from zip(a, b) but only if i == j.
zip(a, b) takes two arrays, a and b, and combines them in such a way that the end result looks like [(1, 9), (2, 8), ...]
So effectively, you're processing the result of zip(a, b) and iterating over tuples i, j to return only if i == j is "truthy". Explaining "truthy" is a bit out of scope for this answer, but in this case, the expression i == j evaluates to True if i and j have the same value. I.e., 5 == 5 is True, where 5 == 4 is False.

This is a list comprehension, which is a python syntax feature. You can read about it in the python tutorial here.
It is being combined with the zip() built-in function. Documentation for that function is here.
A tl;dr is: the zip function makes pairs from both lists, and the list comprehension will filter out the pairs that don't compare equal, selecting only the pairs that match

To get the code working with the random indexing of the elements, you can try the following:
a = [1,2,3,5,4]
b = [9,8,7,6,5]
print [i for i in a for j in b if i == j]
This will output:
[5]

Related

Concise a for loop (x for x in ...) in python

I am trying to concise the following simple loop
a = [1,2,3,2,1,5,6,5,5,5]
for x in set(a):
a.remove(x)
This is working well but I need to know if it is possible to apply the concise for loop like that
a = [x for x in set(a):a.remove(x)]
My desire output is to get or list the duplicates only and get list of them, so the desired output is [1,2,5]
The code is working well
a = [1,2,3,2,1,5,6,5,5,5]
for x in set(a):
a.remove(x)
print(list(set(a)))
My target is not the code but to concise the loop in the loop. I need to learn this trick.
** Found a simple and effective solution:
print(list(set([x for x in a if a.count(x) > 1])))
Original question
a = a if not all([a.remove(i) for i in set(a) ]) else []
print(a)
As suggested by Copperfield, the following also works:
a = any(a.remove(i) for i in set(a) ) or a
Updated question
from collections import Counter
a = [1,2,3,2,1,5,6,5,5,5]
print([k for k, v in Counter(a).items() if v > 1])
find dups in a list
print ([c for i,c in enumerate(a) if a.count(c) > 1 and i==a.index(c)])
The output of this will be:
[1, 2, 5]
alternate for set(a)
Here's the list comprehension to create the same result as
print(list(set(a)))
This can be achieved by doing the following:
print([c for i,c in enumerate(a) if i==a.index(c)])
Here I am checking if the element c is the first time we encountered (index is i) and if yes, then add to list else ignore.
The output of both these will be:
[1, 2, 3, 5, 6]
While the output is the same, I would strongly recommend using the first method than doing a for loop and checking for index. The cost is too high to do this compared to list(set(a))
You can just do:
a = [1,2,3,2,1,5,6,5,5,5]
[a.remove(x) for x in set(a)]
print(a)
a will have the same items as after your for loop.
You can read more about list comprehensions.

How to return the index of the list item

I have the following list in Python:
x = [[1,2],[3,4],[5,'inf'],[6,7],['nan',10]]
How can I return the index of the items that contain 'inf' or 'nan'?
EDIT-1
The first thing I thought about is to make an if-statement, as follows:
if (float('inf') in i) or (float('nan') in i)
But, didn't know how to go about returning the index value.
EDIT-2
The question that has been marked as duplicate didn't actually answer my question.
Thanks.
you can iterate over the list and check for nan or inf:
x = [[1,2],[3,4],[5,'inf'],[6,7],['nan',10]]
for y in x:
if 'inf' in y:
print(x.index(y),y.index('inf'))
elif 'nan' in y:
print(x.index(y), y.index('nan'))
the result will be:
2 1
4 0
I'm glad you added an attempt! We SO users love when folks show their work per say before asking a question. It shows that you are genuinely care about the problem you're trying to solve and not just here for a free-work service.
That being said, I like the simplicity of a list comprehension here:
>>> [(i, j) for i, seq in enumerate(x) for j, item in enumerate(seq) if item == 'inf' or item == 'nan']
[(2, 1), (4, 0)]
A more concise version of the above that takes advantage of sets and their performant membership checks:
>>> [(i, j) for i, seq in enumerate(x) for j, item in enumerate(seq) if item in {'inf', 'nan'}]
What we do here is (1) iterate through the nested list in a nested order, (2) check to see if our item matches the conditions you laid out, (3) capture the index of that position via the result of our enumerate() call.
Suggested Readings:
Python Strings
Enumerate function
[(i, j) for i, outer in enumerate(x) for j, inner in enumerate(outer ) if inner in ['inf','nan']]

Element-wise addition on tuple or list python

I was wondering if anyone can teach me how to do element wise addition on a tuple or list without using zip, numpy arrays, or any of those modules?
For example if I have:
a = (1,0,0,1)
b = (2,1,0,1)
how can i get: (3,1,0,2) instead of (1,0,0,1,2,1,0,1) ?
You can do this using operator.add
from operator import add
>>>map(add, a, b)
[3, 1, 0, 2]
In python3
>>>list(map(add, a, b))
List comprehensions are really useful:
[a[i] + b[i] for i in range(len(a))]
You can use the map function, see here:
https://docs.python.org/2/tutorial/datastructures.html#functional-programming-tools
map(func, seq)
For example:
a,b=(1,0,0,1),(2,1,0,1)
c = map(lambda x,y: x+y,a,b)
print c
This will save you if the length of both lists are not the same:
result = [a[i] + b[i] for i in range(min(len(a), len(b))]
This can be done by simply iterating over the length of list(assuming both the lists have equal length) and adding up the values at that indices in both the lists.
a = (1,0,0,1)
b = (2,1,0,1)
c = (1,3,5,7)
#You can add more lists as well
n = len(a)
#if length of lists is not equal then we can use:
n = min(len(a), len(b), len(c))
#As this would not lead to IndexError
sums = []
for i in xrange(n):
sums.append(a[i] + b[i] + c[i])
print sums
Here is a solution that works well for deep as well as shallow nested lists or tuples
import operator
def list_recur(l1, l2, op = operator.add):
if not l1:
return type(l1)([])
elif isinstance(l1[0], type(l1)):
return type(l1)([list_recur(l1[0], l2[0], op)]) + \
list_recur(l1[1:],l2[1:], op)
else:
return type(l1)([op(l1[0], l2[0])]) + \
list_recur(l1[1:], l2[1:], op)
It (by default) performs element wise addition, but you can specify more complex functions and/or lambdas (provided they are binary)

Make this simple for/if block more pythonic

I need to store in a list the indexes of those values in 3 lists which exceed a given maximum limit. This is what I got:
# Data lists.
a = [3,4,5,12,6,8,78,5,6]
b = [6,4,1,2,8,784,43,6,2]
c = [8,4,32,6,1,7,2,9,23]
# Maximum limit.
max_limit = 20.
# Store indexes in list.
indexes = []
for i, a_elem in enumerate(a):
if a_elem > max_limit or b[i] > max_limit or c[i] > max_limit:
indexes.append(i)
This works but I find it quite ugly. How can I make it more elegant/pythonic?
You could replace your for loop with:
indexes = []
for i, triplet in enumerate(zip(a, b, c)):
if any(e > max_limit for e in triplet):
indexes.append(i)
... which you could then reduce to a list comprehension:
indexes = [i for i, t in enumerate(zip(a, b, c)) if any(e > max_limit for e in t)]
... although that seems a little unwieldy to me - this is really about personal taste, but I prefer to keep listcomps simple; the three-line for loop is clearer in my opinion.
As pointed out by user2357112, you can reduce the apparent complexity of the list comprehension with max():
indexes = [i for i, t in enumerate(zip(a, b, c)) if max(t) > max_limit]
... although this won't short-circuit in the same way that the any() version (and your own code) does, so will probably be slightly slower.
You could try
if max(a_elem, b[i], c[i]) > max_limit:
indexes.append(i)
The logic here is finding out if any one of these three values needs to be greater than max_limit. If the greatest element of these three is greater than max_limit, your condition is satisfied.
I like the exceeders = line best myself
import collections
# Data lists.
a = [3,4,5,12,6,8,78,5,6]
b = [6,4,1,2,8,784,43,6,2]
c = [8,4,32,6,1,7,2,9,23]
Triad = collections.namedtuple('Triad', 'a b c')
triads = [Triad(*args) for args in zip(a, b, c)]
triads = [t for t in zip(a, b, c)] # if you don't need namedtuple
# Maximum limit.
max_limit = 20.
# Store indexes in list.
indexes = [for i, t in enumerate(triads) if max(t) > max_limit]
print indexes
# store the bad triads themselves in a list for
# greater pythonic
exceeders = [t for t in triads if max(t) > max_limit]
print exceeder
As I commented above, using parallel arrays to represent data that are related makes simple code much less simple than it need be.
added in response to comment
Perhaps I gave you too many alternatives, so I shall give only one way instead. One feature that all of the answers have in common is that they fuse the separate "data lists" into rows using zip:
triads = [t for t in zip(a, b, c)]
exceeders = [t for t in triads if max(t) > max_limit]
That's it: two lines. The important point is that storing the index of anything in a list is a C-style way of doing things and you asked for a Pythonic way. Keeping a list of indices means that anytime you want to do something with the data at that index, you have to do an indirection. After those two lines execute, exceeders has the value:
[(5, 1, 32), (8, 784, 7), (78, 43, 2), (6, 2, 23)]
Where each member of the list has the "column" of your three data rows that was found to exceed your limit.
Now you might say "but I really wanted the indices instead". If that is so, there is another part of your problem which you didn't show us which also relies on list indexing. If so, you are still doing things in a C/C++/Java way and "Pythonic" will remain evasive.
>>> maximums = map(max, zip(a, b, c))
>>> [i for i, num in enumerate(maximums) if num > max_limit]
[2, 5, 6, 8]
Old answer
Previously, I posted the mess below. The list comp above is much more manageable.
>>> next(zip(*filter(lambda i: i[1] > max_limit, enumerate(map(max, zip(a, b, c))))))
(2, 5, 6, 8)
m = lambda l: [i for i, e in enumerate(l) if e>max_limit]
indexes = sorted(set(m(a) + m(b) + m(c)))

divide list using recursion

I am trying to implement this function using recursion, the function takes a function parameter f where when passed a value it will return as true or false. It should check all values in the list and store all true values in a list and false values in another list returning them in a tuple.
def divL(f, l):
if not l:
return ([],[])
else:
a = list()
b = list()
for i in range(len(l)):
if f(l[i]):
a.append(l[i])
else:
b.append(l[i])
return (a, b)
Recursive version.
But I agree with others that it is better to do this without recursion.
def divL(f, l):
if not l:
return ([],[])
else:
a, b = divL(f,l[1:])
if f(l[0]):
a.insert(0, l[0])
else:
b.insert(0, l[0])
return (a, b)
#-- test ---
def f(x): return x > 0
divL(f,[1,-2,3,4,2,-5])
([1, 3, 4, 2], [-2, -5])
To solve this problem recursively, then you can start with:
def div_l(fn, elements):
if not elements:
return ([], [])
else:
trues, falses = div_l(fn, elements[1:])
if fn(elements[0]):
trues.append(elements[0])
else:
falses.append(elements[0])
return (trues, falses)
One thing to note about your code, is that, at the moment, there are no recursive calls. In the code above, our base case is when the list of elements is empty (if not elements). Then, we constantly check the first element of the list to see if it satisfies fn, appending it to trues or falses appropriately. Then we pass all the elements of the list besides the first one (elements[1:]) to the function again and repeat until the list is empty.
Having said that, the problem does not seem to be recursive in nature. Why not just use list comprehensions:
a = [element for element in l if f(element)]
b = [element for element in l if not f(element)]
Also, names like a, b, l and f are not great because they say nothing about what they actually mean.
Regarding the way you have written your code so far, the Pythonic way to iterate through the elements of a list is not the same as languages like C/C++/Java/etc; you should rarely need to get list items using their index. Instead you can re-write your for statement as follows:
for element in l:
if f(element):
a.append(element)
else:
b.append(element)
P.S. I haven't tested the code above yet, but it should be a reasonable starting point.
Recursive function that takes split lists as parameters:
def _R(f, l, ts, fs):
if not l:
return ts, fs
if f(l[0]):
return _R(f, l[1:], ts + l[:1], fs)
return _R(f, l[1:], ts, fs + l[:1])
def R(f, l):
return _R(f, l, [], [])
print R( lambda x: x > 10, range(20) )

Categories