I have two lists say
A = [1,3]
B = [1,3,5,6]
I want to know the index of the first differing element between these lists (2 in this case).
Is there a simple way to do this, or do I need to write a loop?
You can use following generator expression within next() function using enumerate() and zip() function:
>>> next(ind for ind,(i,j) in enumerate(zip(A,B)) if i != j)
2
Perhaps the loop you mentioned is the most obvious way, not necessarily the most pretty. Still every O(n) complexity solution is fine by me.
lesser_length = min(len(A), len(B))
answer = lesser_length # If one of the lists is shorter and a sublist,
# this will be the answer, because the if condition
# will never be satisfied.
for i in xrange(lesser_length):
if A[i] != B[i]:
answer = i
break
range instead of xrange in Python3. A generator would be the best way given that you don't know when the difference between lists will occur.(In Python2, xrange is generator. In Python3, xrange became the regular range() function.)
A list comprehension is also viable. I find this to be more readable.
Related
I am trying to call a function for a range of values. That function returns a list. The goal is to combine all the returned lists into a list.
Here is a test function that returns a list:
def f(i):
return [chr(ord('a') + i), chr(ord('b') + i), chr(ord('c') + i)]
Here is a list comprehension that does what I need that I came up with after some experimentation and a lot of StackOverflow reading:
y = [a for x in (f(i) for i in range(5)) for a in x]
However, I do not understand why and how it works when a simple loop that solves this problem looks like this:
y = []
for x in (f(i) for i in range(5)):
for a in x:
y.append(a)
Can someone explain?
Thanks!
This may be a better illustration, following Bendik Knapstad's answer:
[
a # element added to the list
for x in (f(i) for i in range(5)) # outer loop
for a in x # inner loop that assigns to element to be added to the list
]
Answering to this:
However, I do not understand why and how it works (list comprehensions) when a simple loop that solves this problem looks like this (for loops)
Yes, they both can work but there are some differences.
First, with list comprehensions, you are able to generate a list (because that's the output) after assigning it to a variable. Whereas in a for loop you must have the list created (regardless if it's empty or not) if you wish to use append later on perform any updating/deleting/re-indexing operation.
Second, simplicity. While for loops might be used in complex tasks where you need to apply a wide variety of functions, and maybe use RNGs, list comprehensions are always preferrable when it comes to dealing with lists and performing rather 'basic' operations (of course you can start nesting them and turn them into something more complex).
Third and finally, speed. List comprehensions tend to perform baster when compared to for loops for simple tasks.
More in-depth information regarding listcomp and for loops can be read in python's official tutorial. https://docs.python.org/3/tutorial/datastructures.html
Nested list comprehensions are hard to read.
But if you look at the two expressions you'll se that they contain the same logic.
In the list comprehension the first a is the part you want to keep in the list. It's equal to the y.append(a) in the for loop.
The for x in (f(i) for i in range(5)) is the same as in your for loop
The same goes for the next line for a in x
So for x in (f(i) for i in range(5)) creates a list x
So if we had the list x already we could write
y= [a for a in x]
Basically, I am wondering what is the most efficient method to find the elements of a python list with a value of greater than, say, n.
I believe, the easiest, yet not so efficient, way is as below,
for i in range(len(theList)):
if theList[i] > n:
subList.append(theList[i])
Moreover, we have the single line for as below,
(subList for subList in theList if sublist > n)
(Please correct me if there is anything wrong with the above syntax)
Finally, we can use filter() function, which is not pleasant to use, at least for me.
The above methods were all the ways that I know. If you know any better method please tell me. Otherwise, please explain which one is the best, in the sense of efficiency and run-time.
There is no always right answer to this and there have been a few SO posts about the speed of different approaches when handling list, see e.g. here, here or here.
What is the fastest way might depend a lot on your list.
This said, let's just have a look at how fast the suggested approaches are.
For simple comparisons like this you can use timeit:
1. Case: The for-loop
for_case = """newList=[]
for x in theList:
if x > n:
newList.append(x)"""
2. Case: List comprehension
list_comp = '[x for x in theList if x > n]'
3. Case: The filter (somehow unliked)
filtering = 'list(filter(lambda x: x > n, theList))'
Some preparation:
import timeit
si = 'theList=range(2000);n=1000;' # using list(range(2000)) has no effect on the ranking
So let's see:
timeit.timeit(si+list_comp, number=10000)
Out[21]: 1.3985847820003983
timeit.timeit(si+filtering, number=10000)
Out[22]: 3.315784254024038
timeit.timeit(si+for_case, number=10000)
Out[23]: 2.0093530920275953
So, at least on my machine, the list comprehension takes it away, followed by the for-loop and, at least in this case the unliked filter is indeed the slowest.
list comprehension version:
sublist = [ i for i in the_list if i > n ]
Generator expression: ( if the list is huge size)
sublist = ( i for i in the_list if i > n )
The following is a simplified example of my code.
>>> def action(num):
print "Number is", num
>>> items = [1, 3, 6]
>>> for i in [j for j in items if j > 4]:
action(i)
Number is 6
My question is the following: is it bad practice (for reasons such as code clarity) to simply replace the for loop with a comprehension which will still call the action function? That is:
>>> (action(j) for j in items if j > 2)
Number is 6
This shouldn't use a generator or comprehension at all.
def action(num):
print "Number is", num
items = [1, 3, 6]
for j in items:
if j > 4:
action(i)
Generators evaluate lazily. The expression (action(j) for j in items if j > 2) will merely return a generator expression to the caller. Nothing will happen in it unless you explicitly exhaust it. List comprehensions evaluate eagerly, but, in this particular case, you are left with a list with no purpose. Just use a regular loop.
This is bad practice. Firstly, your code fragment does not produce the desired output. You would instead get something like: <generator object <genexpr> at 0x03D826F0>.
Secondly, a list comprehension is for creating sequences, and generators a for creating streams of objects. Typically, they do not have side effects. Your action function is a prime example of a side effect -- it prints its input and returns nothing. Rather, a generator should for each item it generates, take an input and compute some output. eg.
doubled_odds = [x*2 for x in range(10) if x % 2 != 0]
By using a generator you are obfuscating the purpose of your code, which is to mutate global state (printing something), and not to create a stream of objects.
Whereas, just using a for loop makes the code slightly longer (basically just more whitespace), but immediately you can see that the purpose is to apply function to a selection of items (as opposed to creating a new stream/list of items).
for i in items:
if i < 4:
action(i)
Remember that generators are still looping constructs and that the underlying bytecode is more or less the same (if anything, generators are marginally less efficient), and you lose clarity. Generators and list comprehensions are great, but this is not the right situation for them.
While I personally favour Tigerhawk's solution, there might be a middle ground between his and willywonkadailyblah's solution (now deleted).
One of willywonkadailyblah's points was:
Why create a new list instead of just using the old one? You already have the condition to filter out the correct elements, so why put them away in memory and come back for them?
One way to avoid this problem is to use lazy evaluation of the filtering i.e. have the filtering done only when iterating using the for loop by making the filtering part of a generator expression rather than a list comprehension:
for i in (j for j in items if j > 4):
action(i)
Output
Number is 6
In all honesty, I think Tigerhawk's solution is the best for this, though. This is just one possible alternative.
The reason that I proposed this is that it reminds me a lot of LINQ queries in C#, where you define a lazy way to extract, filter and project elements from a sequence in one statement (the LINQ expression) and can then use a separate for each loop with that query to perform some action on each element.
Is there an equivalent of starts with for lists in python ?
I would like to know if a list a starts with a list b. like
len(a) >= len(b) and a[:len(b)] == b ?
You can just write a[:len(b)] == b
if len(b) > len(a), no error will be raised.
For large lists, this will be more efficient:
from itertools import izip
...
result = all(x==y for (x, y) in izip(a, b))
For small lists, your code is fine. The length check can be omitted, as DavidK said, but it would not make a big difference.
PS: No, there's no build-in to check if a list starts with another list, but as you already know, it's trivial to write such a function yourself.
it does not get much simpler than what you have (and the check on the lengths is not even needed)...
for an overview of more extended/elegant options for finding sublists in lists, you can check out the main answer to this
post : elegant find sub-list in list
Is there a way for list comprehending this condition set.
clamp_sel = list()
for i in range(0, len(clamp_normalized)):
for j in range(0, len(clamp_normalized[i])):
if clamp_normalized[i][j][0] < int(max_min_band[index_selec]):
clamp_sel.append(int(clamp_normalized[i][j][0]))
If it is single dimension list, I could set the condition in this way.
norm_sel = [i for i in normalize_data if i[0] > max_min_band[index_selec]]
Thanks
If clamp_normalized is a list of lists, you can iterate without using range, unless you need the index.
clamp_sel = [j[0]
for i in clamp_normalized
for j in i
if j[0] < int(max_min_band[index_selec])]
This should translate directly into a list-comprehension:
clamp_sel = [int(clamp_normalized[i][j][0])
for i in range(0, len(clamp_normalized))
for j in range(0, len(clamp_normalized[i]))
if clamp_normalized[i][j][0] < int(max_min_band[index_selec])]
The general rule is (see the manual) that you should write the list-comprehension in exactly the same order as you would do with a series of nested for loops and if-statements. The only thing you change is to replace the final xx.append(yy) with just yy at the front of the list comprehension. Also note that this is essentially one long expression that you could write on an extremely long line. Because of the enclosing [], you can divide this expression over multiple lines, with arbitrary indentation.
If the list-comprehension is more pythonic than the original is a question of taste. In this case, the nesting is straightforward, so I would personally go for the list-comprehension. If it gets more complex, stick to the for-loops.
As thefourtheye shows, this example can be further simplified by replacing the use of range() with a direct iteration over your lists.