List comprehension - python

I have some trouble with list comprehension, I think I already know how to use it well but certainly I don't.
So here is my code:
vector1=[x for x in range(0,351,10)]
first=list(range(0,91))
second=list(range(100,181))
third=list(range(190,271))
fourth=list(range(280,351))
Quadrants=first+second+third+fourth
string=['First']*91+['Second']*81+['Third']*81+['Fourth']*71
vector2=dict(zip(Quadrants,string))
Quadrant=[]
for n in range (len(vector1)):
Quadrant+=[vector2[vector1[n])]]
So i want to do the for_loop with list comprehension, but i can't... I tried this:
Quadrant=[y3 for y3 in [vector2[vector1[i]]] for i in range (len(vector1))]

Here's the code you're trying to convert to a listcomp:
Quadrant=[]
for n in range (len(vector1)):
Quadrant+=[y[vector1[n]]]
First, you have to convert that into a form using append. There's really no reason to build a 1-element list out of y[vector1[n]] in the first place, so just scrap that and we have something we can appenddirectly:
Quadrant=[]
for n in range(len(vector1)):
Quadrant.append(y[vector1[n]])
And now, we have something we can convert directly into a list comprehension:
Quadrant = [y[vector1[n]] for n in range(len(vector1))]
That's all there is to it.
However, I'm not sure why you're doing for n in range(len(vector1)) in the first place if the only thing you need n for is vector1[n]. Just loop over vector1 directly:
Quadrant=[]
for value in vector1:
Quadrant.append(y[value])
Which, again, can be converted directly:
Quadrant = [y[value] for value in vector1]
However, all of this assumes that your original explicit loop is correct in the first place, which obviously it isn't. Your vector1 is a dict, not a list. Looping over it the keys from 0 to len(vector1) is just going to raise KeyErrors all over the place. Changing it to loop directly over vector1 is going to solve that problem, but it means you're looping over the keys. So… I have no idea what your code was actually trying to do, but get the simple but verbose version right first, and you can probably convert it to a comprehension just as easily as the above.

Related

Convert for loop into list comprehension with assignment?

I am trying to convert a for loop with an assignment into a list comprehension.
More precisely I am trying to only replace one element from a list with three indexes.
Can it be done?
for i in range(len(data)):
data[i][0] = data[i][0].replace('+00:00','Z').replace(' ','T')
Best
If you really, really want to convert it to a list comprehension, you could try something like this, assuming the sub-lists have three elements, as you stated in the questions:
new_data = [[a.replace('+00:00','Z').replace(' ','T'), b, c] for (a, b, c) in data]
Note that this does not modify the existing list, but creates a new list, though. However, in this case I'd just stick with a regular for loop, which much better conveys what you are actually doing. Instead of iterating the indices, you could iterate the elements directly, though:
for x in data:
x[0] = x[0].replace('+00:00','Z').replace(' ','T')
I believe it could be done, but that's not the best way to do that.
First you would create a big Jones Complexity for a foreign reader of your code.
Second you would exceed preferred amount of chars on a line, which is 80. Which again will bring complexity problems for a reader.
Third is that list comprehension made to return things from comprehensing of lists, here you change your original list. Not the best practice as well.
List comprehension is useful when making lists. So, it is not recommended here. But still, you can try this simple solution -
print([ele[0].replace('+00:00','Z').replace(' ','T') for ele in data])
Although I don't recommend you use list-comprehension in this case, but if you really want to use it, here is a example.
It can handle different length of data, if you need it.
code:
data = [["1 +00:00",""],["2 +00:00","",""],["3 +00:00"]]
print([[i[0].replace('+00:00','Z').replace(' ','T'),*i[1:]] for i in data])
result:
[['1TZ', ''], ['2TZ', '', ''], ['3TZ']]

Creating a Python list comprehension with an if and break with nested for loops

I noticed from this answer that the code
for i in userInput:
if i in wordsTask:
a = i
break
can be written as a list comprehension in the following way:
next([i for i in userInput if i in wordsTask])
I have a similar problem which is that I would like to write the following (simplified from original problem) code in terms of a list comprehension:
for i in xrange(N):
point = Point(long_list[i],lat_list[i])
for feature in feature_list:
polygon = shape(feature['geometry'])
if polygon.contains(point):
new_list.append(feature['properties'])
break
I expect each point to be associated with a single polygon from the feature list. Hence, once a polygon that contains the point is found, break is used to move on to the next point. Therefore, new_list will have exactly N elements.
I wrote it as a list comprehension as follows:
new_list = [feature['properties'] for i in xrange(1000) for feature in feature_list if shape(feature['geometry']).contains(Point(long_list[i],lat_list[i])]
Of course, this doesn't take into account the break in the if statement, and therefore takes significantly longer than using nested for loops. Using the advice from the above-linked post (which I probably don't fully understand), I did
new_list2 = next(feature['properties'] for i in xrange(1000) for feature in feature_list if shape(feature['geometry']).contains(Point(long_list[i],lat_list[i]))
However, new_list2 has much fewer than N elements (in my case, N=1000 and new_list2 had only 5 elements)
Question 1: Is it even worth doing this as a list comprehension? The only reason is that I read that list comprehensions are usually a bit faster than nested for loops. With 2 million data points, every second counts.
Question 2: If so, how would I go about incorporating the break statement in a list comprehension?
Question 3: What was the error going on with using next in the way I was doing?
Thank you so much for your time and kind help.
List comprehensions are not necessarily faster than a for loop. If you have a pattern like:
some_var = []
for ...:
if ...:
some_var.append(some_other_var)
then yes, the list comprehension is faster than the bunch of .append()s. You have extenuating circumstances, however. For one thing, it is actually a generator expression in the case of next(...) because it doesn't have the [ and ] around it.
You aren't actually creating a list (and therefore not using .append()). You are merely getting one value.
Your generator calls Point(long_list[i], lat_list[i]) once for each feature for each i in xrange(N), whereas the loop calls it only once for each i.
and, of course, your generator expression doesn't work.
Why doesn't your generator expression work? Because it finds only the first value overall. The loop, on the other hand, finds the first value for each i. You see the difference? The generator expression breaks out of both loops, but the for loop breaks out of only the inner one.
If you want a slight improvement in performance, use itertools.izip() (or just zip() in Python 3):
from itertools import izip
for long, lat in izip(long_list, lat_list):
point = Point(long, lat)
...
I don't know that complex list comprehensions or generator expressions are that much faster than nested loops if they're running the same algorithm (e.g. visiting the same number of values). To get a definitive answer you should probably try to implement a solution both ways and test to see which is faster for your real data.
As for how to short-circuit the inner loop but not the outer one, you'll need to put the next call inside the main list comprehension, with a separate generator expression inside of it:
new_list = [next(feature['properties'] for feature in feature_list
if shape(feature['shape']).contains(Point(long, lat)))
for long, lat in zip(long_list, lat_list)]
I've changed up one other thing: Rather than indexing long_list and lat_list with indexes from a range I'm using zip to iterate over them in parallel.
Note that if creating the Point objects over and over ends up taking too much time, you can streamline that part of the code by adding in another nested generator expression that creates the points and lets you bind them to a (reusable) name:
new_list = [next(feature['properties'] for feature in feature_list
if shape(feature['shape']).contains(point))
for point in (Point(long, lat) for long, lat in zip(long_list, lat_list))]

python list modification to list of lists

I am trying to learn python (just finished Learn Python the Hard Way book!), but I seem to be struggling a bit with lists. Specifically speaking, I have a list like so:
x = ["/2.ext", "/4.ext", "/5.ext", "/1.ext"]
I would like to operate on this above list, so that it returns a list (somehow!) like so:
y = [ ["/1.ext", "/2.ext"], ["/1.ext", "/2.ext", "/3.ext, "/4.ext"], ["/1.ext", "/2.ext", "/3.ext", "/4.ext", "/5.ext"], ["/1.ext"] ]
So, essentially, each element in x is now turned to a list of lists. I could probably loop over x, store all the sequence lists in another list and then merge then together - but it just seems like there must be a better way to do it.
Would be grateful if someone could point me in the right direction to solve this problem.
EDIT (taking into account Martijn's comments):
Specifically, I want to generate the intermediary filenames in a sequence, ending at the number for each x list element
You can do it as follows:
x = ["/2.ext", "/4.ext", "/5.ext", "/1.ext"]
print [['/{}.ext'.format(j) for j in range(1,int(i[1])+1)] for i in x]
[OUTPUT]
[['/1.ext', '/2.ext'], ['/1.ext', '/2.ext', '/3.ext', '/4.ext'], ['/1.ext', '/2.ext', '/3.ext', '/4.ext', '/5.ext'], ['/1.ext']]
This only works for digits upto 9. I'll post update for more general solutions
HERE is the more general solution. Works for any numbers:
import re
x = ["/2.ext", "/4.ext", "/5.ext", "/1.ext"]
print [['/{}.ext'.format(j) for j in range(1,int(re.search(r'\d+',i).group(0))+1)] for i in x]

Access prior element in a list comprehension

I've looked around into list comprehensions on here, and can find a solution for this.
Closest I've come is questions that advice using enumerate() while iterating in a list comprehension.
What I'd like to do is access the prior element in a list comprehension. The context is this question on another StackExchange site.
Basically, if I get this string from stdin - 4,6,+2,+8, I'd like to output the array that is the integer of the number, or the number plus the value of the prior element. In the given case the final array is: 4,6,8,16 - or 4,6,6+2,(6+2)+8. Going from left to right with an array and for loop its trivial to generate, but I'm curious to know if it can be done in a single list comprehension.
As for what I have:
from sys import*
s=argv[1].split(',')
for i in range(len(s)):
s[i]=eval(`s[i-1]`*(s[i][0]=='+')+s[i])
print s
Which prints the correct result, however the following list comprehension does not:
s=argv[1].split(',')
s=[eval(s[i-1]*(x[0]=='+')+x) for i,x in enumerate(s)]
print s
Which results in:
[4, 6, 8, 10]
Which is equivilient to 4, 6, +2+6, +2+8.
What I'd like to be able to do finally is something along the lines of:
s=[eval(PRIOR_VALUE*(x[0]=='+')+x) for x in argv[1].split(',')]
So can I easily access the prior computed element of a list comprehension in the same list comprehension?
So can I easily access the prior computed element of a list comprehension in the same list comprehension?
Easily? No.
If you want to make things difficult for yourself, there are plenty of ways to save state information in a list comprehension. For example,
prev = [None]
s = [prev.__setitem__(0, val) or val
for item in list
for val in [something(item, prev[0])]]
This is not a good idea.

Insert number to a list

I have an ordered dictionary like following:
source =([('a',[1,2,3,4,5,6,7,11,13,17]),('b',[1,2,3,12])])
I want to calculate the length of each key's value first, then calculate the sqrt of it, say it is L.
Insert L to the positions which can be divided without remainder and insert "1" after other number.
For example, source['a'] = [1,2,3,4,5,6,7,11,13,17] the length of it is 9.
Thus sqrt of len(source['a']) is 3.
Insert number 3 at the position which can be divided exactly by 3 (eg. position 3, position 6, position 9) if the position of the number can not be divided exactly by 3 then insert 1 after it.
To get a result like folloing:
result=([('a',["1,1","2,1","3,3","4,1","5,1","6,3","7,1","11,1","13,3","10,1"]),('b',["1,1","2,2","3,1","12,2"])]
I dont know how to change the item in the list to a string pair. BTW, this is not my homework assignment, I was trying to build a boolean retrival engine, the source data is too big, so I just created a simple sample here to explain what I want to achive :)
As this seems to be a homework, I will try to help you with the part you are facing problem with
I dont know how to change the item in the list to a string pair.
As the entire list needs to be updated, its better to recreate it rather than update it in place, though its possible as lists are mutable
Consider a list
lst = [1,2,3,4,5]
to convert it to a list of strings, you can use list comprehension
lst = [str(e) for e in lst]
You may also use built-in map as map(str,lst), but you need to remember than in Py3.X, map returns a map object, so it needs to be handled accordingly
Condition in a comprehension is best expressed as a conditional statement
<TRUE-STATEMENT> if <condition> else <FALSE-STATEMENT>
To get the index of any item in a list, your best bet is to use the built-in enumerate
If you need to create a formatted string expression from a sequence of items, its suggested to use the format string specifier
"{},{}".format(a,b)
The length of any sequence including a list can be calculated through the built-in len
You can use the operator ** with fractional power or use the math module and invoke the sqrt function to calculate the square-root
Now you just have to combine each of the above suggestion to solve your problem.

Categories

Resources