Recently, I've found a code for developing a set of lists from a list, that code was written by user Mai, answering question but i have not understood it yet. Could somebody help me to understand it? And... is there a way to rewrite that code that be easier? The code is:
def even_divide(lst, num_piece=4):
return [
[lst[i] for i in range(len(lst)) if (i % num_piece) == r]
for r in range(num_piece)
]
Thanks!
It's pretty simple actually. Just follow through the values of the two loops:
Starting with the outer loop, r would be 0, then 1, then 2, etc. Let's look at the case for which r == 1. When running through the different values of i, (which would be 0, 1, 2, ... len(lst), the value of i % 4, meaning the remainder of dividing i by 4, would be 0, 1, 2, 3, 0, 1, 2, 3, .... So the i % 4 would be equal to r, for every 4 values of i!
For our chosen r == 1, that would mean we're choosing lst[1], lst[5], lst[9], ..., etc.
And for r == 2? You guessed it! You'd be picking up lst[2], lst[6], lst[10],....
So over all you'd get 4 lists, with non-overlapping elements of the original list, by just "jumping" 4 elements every time, but starting at different values.
Which naturally leads to the more simple solution:
def even_divide(lst, num_piece=4):
return [lst[r::num_piece] for r in range(num_piece)]
Could somebody help me to understand it?
Sure! It's a list comprehension. A list comprehension takes a list and does something to or with every element in that list. Let's say I want to multiply every element in my list by 2:
new_list = [element*2 for element in my_list]
What makes it a list comprehension is the bracket syntax. For those new to it, that's usually the part that takes a moment to get used to. With that said, I assume that is what is giving you difficulty in understanding the code in your question, as you have a list comprehension in a list comprehension. It might be difficult to understand now, but list comprehensions are a wonderful thing in python.
But, as this post mentions, there's a lot of discussions around list comprehension, lambda's, map, reduce, and filter. Ultimately, its up to you to decide what's best for your project. I'm not a fan of anything else but list comprehensions, so I use those religiously.
Based on the question you've linked, the list comprehension takes a 1d list of length x and turns it into a 2d list of (length x, width y). It's like numpy.reshape.
And... is there a way to rewrite that code [to] be easier?
I would not recommend it. List comprehensions are considered very pythonic and you will see them everywhere. Best to use them and get used to them.
Related
I've seen this question before, but it only deals with recursions that are linear in nature. I'm looking for something more general.
Suppose I have the following code
n = 10
num_bits = [0]
for i in range(n):
nums_bits.append(num_bits[i>>1]+i%2)
This code will compute num_bits, a 11 element array of value with num_bits[i] representing the number of bits it takes to represent i.
Is it possible to write this as a list comprehension? Something like this doesn't work
num_bits = [0]*11
num_bits = [num_bits[i>>1]+i%2 for i in range(11)]
since the comprehension doesn't update the value of num_bits in the middle of evaluation. Is there a canonical way to do something like this, besides a for loop?
P.S. I'm aware there are other ways to solve this problem: I'm just using it as a vehicle to understand Python's features better.
Edit: To summarize, I'd like to know what the proper way of generating lists of values that are dependent on previous values is. For a simpler example, consider the Fibonacci Numbers
fibonacci = [0,1]
for i in range(10):
fibonacci.append(fibonacci[-1]+fibonacci[-2])
Is there a way to generate these numbers in a comprehension? If not, what tools are there for this other than for loops (or are for/while loops my only option)?
Given it is not a piece of code I'd recommend, for the reasons discussed in the comments above and in the other answer, this comprehension should be faster than the for loop:
fibonacci = [0,1]
deque((fibonacci.append(fibonacci[-1]+fibonacci[-2]) for _ in range(10)), maxlen=0)
as it fills the list consuming the generator and discarding the result (an empty queue, it's the fastest recommended way to consume an iterator)
It produces:
>>> fibonacci
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
No.
There is no nice way to do this with a list comprehension, and that is not what they’re for. The purpose of list comprehensions is to offer a more readable alternative to maps and filters, and this isn’t that, so it’s not possible to do this in a sensible way.
Tell me the difference and benefits of u both using list with index and list without index?
li=[1,2,'ayush',9,10,11,'yaman']
for i in range(len(li)):
print(li[i])
for ele in li:
print(ele)
The difference is that in first case you can modify the original list and in the second case you cannot:
li=[1,2,'ayush',9,10,11,'yaman']
for ele in li:
ele = 1
print(li)
for i in range(len(li)):
li[i] = 1
print(li)
yields
[1, 2, 'ayush', 9, 10, 11, 'yaman']
[1, 1, 1, 1, 1, 1, 1]
I suggest you read about Python lists.
Besides the mentioned fact that if you are not iterating over the list, but using indices instead, you are able to modify it on the fly, there is a slight speed advantage when iterating over the list directly.
This is because, if you use range(), Python constructs a fresh iterator, or in Python's 2.x case, another list, which takes extra time. Then, in each loop iteration, you are accessing a new index again and again. Random access in Python lists is very fast, but, if you iterate directly over the list, Python makes you an iterator from the list itself which assigns the next() element's value to the variable specified in the for loop and this all happens on the C level, so it is much faster. All you have to do is access the loops variable which is just a little bit faster than accessing an indexed element of a list. This is because random access to the list elements will be performed part in Python and better part on C level.
So, if you do not have to change anything, but only need access to the elements, iterate over the list. If you are making in-place changes, then use indices.
If you need both, as in, you need to skip some elements or something, best way to go about it is:
new_list = []
x = 0
for element in the_list:
if x%2==0:
new_list.append(element)
x += 1
Of course, this is just a stupid example. There is much better and faster way in Python to perform the task the loop above does. Some might argue that I could have used enumerate() for the example above, but I find this way cleaner and faster.
So I know that to get a single column, I'd have to write
a = list(zip(*f)[0])
and the resulting a will be a list containing the first element in the lists in f.
How do I do this to get more than one element per list? I tried
a = list(zip(*f)[1:19])
But it just returned a list of lists where the inner list is the composed of the ith element in every list.
The easy way is not to use zip(). Instead, use a list comprehension:
a = [sub[1:19] for sub in f]
If it is actually the second half that you are looking for:
a = [sub[len(sub) // 2:] for sub in f]
That will include the 3 in [1, 2, 3, 4, 5]. If you don't want to include it:
a = [sub[(len(sub) + 1) // 2:] for sub in f]
You should definitely prefer #zondo's solution for both performance and readability. However, a zip based solution is possible and would look as follows (in Python 2):
zip(*zip(*f)[1:19])
You should not consider this cycle of unpacking, zipping, slicing, unpacking and re-zipping in any serious code though ;)
In Python 3, you would have to cast both zip results to list, making this even less sexy.
This question already has answers here:
Why does this iterative list-growing code give IndexError: list assignment index out of range? How can I repeatedly add (append) elements to a list?
(9 answers)
Closed 4 months ago.
This is such a simple issue that I don't know what I'm doing wrong. Basically I want to iterate through the items in an empty list and increase each one according to some criteria. This is an example of what I'm trying to do:
list1 = []
for i in range(5):
list1[i] = list1[i] + 2*i
This fails with an list index out of range error and I'm stuck. The expected result (what I'm aiming at) would be a list with values:
[0, 2, 4, 6, 8]
Just to be more clear: I'm not after producing that particular list. The question is about how can I modify items of an empty list in a recursive way. As gnibbler showed below, initializing the list was the answer. Cheers.
Ruby (for example) lets you assign items beyond the end of the list. Python doesn't - you would have to initialise list1 like this
list1 = [0] * 5
So when doing this you are actually using i so you can just do your math to i and just set it to do that. there is no need to try and do the math to what is going to be in the list when you already have i. So just do list comprehension:
list1 = [2*i for i in range(5)]
Since you say that it is more complex, just don't use list comprehension, edit your for loop as such:
for i in range(5):
x = 2*i
list1[i] = x
This way you can keep doing things until you finally have the outcome you want, store it in a variable, and set it accordingly! You could also do list1.append(x), which I actually prefer because it will work with any list even if it's not in order like a list made with range
Edit: Since you want to be able to manipulate the array like you do, I would suggest using numpy! There is this great thing called vectorize so you can actually apply a function to a 1D array:
import numpy as np
list1 = range(5)
def my_func(x):
y = x * 2
vfunc = np.vectorize(my_func)
vfunc(list1)
>>> array([0, 2, 4, 6, 8])
I would advise only using this for more complex functions, because you can use numpy broadcasting for easy things like multiplying by two.
Your list is empty, so when you try to read an element of the list (right hand side of this line)
list1[i] = list1[i] + 2*i
it doesn't exist, so you get the error message.
You may also wish to consider using numpy. The multiplication operation is overloaded to be performed on each element of the array. Depending on the size of your list and the operations you plan to perform on it, using numpy very well may be the most efficient approach.
Example:
>>> import numpy
>>> 2 * numpy.arange(5)
array([0, 2, 4, 6, 8])
I would instead write
for i in range(5):
list1.append(2*i)
Yet another way to do this is to use the append method on your list. The reason you're getting an out of range error is because you're saying:
list1 = []
list1.__getitem__(0)
and then manipulate this item, BUT that item does not exist since your made an empty list.
Proof of concept:
list1 = []
list1[1]
IndexError: list index out of range
We can, however, append new stuff to this list like so:
list1 = []
for i in range(5):
list1.append(i * 2)
This should be trivial. Yet I don't feel 100% sure about my trick.
I have a list of lists (lol ;)) that captures edge relationships between nodes of a graph. Let's say I have a directed graph with 4 nodes labeled 0, 1, 2, 3. The edges are {(0,2),(0,3),(1,0),(1,3),(2,1)} and so the adjacency lol (call it a) is
a = [[2,3],[0,3],[1],[]]
I want to find the incidence lol now, i.e. a list of lists which indicate which nodes are incident on which nodes. For this example, the incidence lol (call it b) would be:
[[1], [2], [0], [0, 1]]
I tried the following code:
b = [[],[],[],[]]
[b[j].append(i) for i,x in enumerate(a) for j in x]
This gives me the right incidence matrix b.
The second step, although works, should ideally be b[j].append(i) for i,x in enumerate(a) for j in x, without the opening [ and closing ]. But Python interpreter cries syntax error without it. Is there a better way of phrasing it?
Your question is essentially about using list comprehensions for side effects. As, e.g. the answers to this question say, breaking it down into a for loop (or loops) is the way to go:
for i, x in enumerate(a):
for j in x:
b[j].append(i)
Also, please note that list comprehensions are used to construct lists in a very natural, easy way, like a mathematician is used to do. That is why, in Python, syntax requires square brackets (in your case).