Different way to create list within a list [duplicate] - python

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 1 year ago.
I have a question about how to create a list within a list. For example, we use
[[0] * n for _ in range(m)]
to create a matrix with dimension n*m. However, when I tried the similar thing to create a nested list.
l = [[(0, 1)]] * n
And then update the l[0], I find the l[1] would also get updated. However, the approach l = [[(0, 1)] for _ in range(n)] produces what I need.
Would you please help me understand this issue?
Thanks for your help.
Follow up:
In the dynamic programming, we use [-math.inf]*n to construct a list and then update the values. Why this approach works, but the above approach failed? Thanks.

The base list comprehension format
[0 for i in range(some_value)]
does create a list with some_value number of 0s. Using _ instead if i just discards it directly.
Now if you want a nested list, first question is, if outer list is rows or columns, however since most often it is matrix[row][column] i will go with it. So lets say you want 10 rows you need 10 items so
[row for i in range(number_rows)]
now what is your definition of a row
[0 for i in range(number_columns)]
if it is initialized with 0s.
Putting the two together, we get
columns, rows = 3,5
l = [[0 for i in range (columns)] for j in range(rows)]
EDIT:
Why [[[0] * n] * m] doesn't work:
When you multiply a mutable object, such as a list, it is not copied, but referenced, meaning, both list entries do point to the same variable in memory. The problem is referred to as deep copy vs shallow copy. It can be observed by calling id of an object.
l = [[1,2,3]]*3
Out[43]: [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
[id(i) for i in l]
Out[44]: [139708443372224, 139708443372224, 139708443372224]
As you can see, all items refer to the same variable, and hence, changing one changes all.

Related

How to iterate and modify over a numpy array?

I have an array that I would like to iterate over and modify the array itself, through inserts or deletions.
for idx, ele in enumerate(np.nditer(array)):
if idx + 1 < array.shape[0] and ele > array[idx+1]:
array = np.delete(array, idx+1)
print(ele)
Given [5, 4, 3, 2, 1] I want the loop to print out 5 3 1 because 4 and 2 are smaller than their previous elements. But because python creates an iterator based on the first instance of array so it prints 5 4 3 2 1. I want to know if I can get
Generally speaking I want the iterator to be modified if I modify the array within the body of my loop.
You cannot mutate the length of a numpy array, because numpy assigns the required memory for an array upon its creation.
With
array = np.delete(array, idx+1)
You are creating a new array on the right hand side of the = and reassign the name array.
The return value for enumerate(np.nditer(array)) has already been created at that point and won't recognize that the name array has been rebound.
In principle, you can iterate over a sequence and mutate its length at the same time (generally not a good idea). The object just needs to have methods that let you mutate its length (like lists, for example).
Consider:
>>> l = [5, 4, 3, 2, 1]
>>> for idx, ele in enumerate(l):
...: if ele == 3:
...: l.pop(idx) # mutates l
...: print(ele)
...:
5
4
3
1
>>> l
[5, 4, 2, 1]
Notice that
l is mutated.
The loop does not print 2 because popping an element reduces the indexes of all the remaining elements by one. Now l[2] == 2, but index 2 has already been visited by the iterator, so the next print-call prints l[3] which is 1.
This proves that mutations to l have effect on subsequent iterations.
Instead of looping over an array, you can use where method to find
indices of elements meeting some condition.
Then to delete the selected element (or elements), you can use
delete method, passing the source array, and a list of indices.
Then save the result, e.g. under the same variable.
To add an element, you can use append or insert methods
(for details see Numpy documentation).
I have also found a SO post concerning how to loop and delete over an array.
See Deleting elements from numpy array with iteration

How to add and display vertical elements of randomly generated list of lists in python?

The following simple code will generate a list of lists of three elements and the total of the elements in each list on the same line.
import random
for i in xrange(3):
my_randoms1 = [random.randrange(1,6,1) for _ in range (3)]
print my_randoms1,"\t", sum(my_randoms1)
Now the question is: How could I add the vertical corresponding elements of all lists and display the sums in a new list on top of all lists as [T1,T2,T3]? Thanks
[T1,T2,T3]
[1, 1, 2] 4
[5, 3, 4] 12
[5, 1, 5] 11
If you're going to be doing anything else with these lists, you probably want to store them in some sort of data structure, possibly using Numpy to place them in a matrix.
The simplest way to do just this is to have some sort of variable, say col_sums = [0,0,0] (initialized before your loop) and add the values of each random list as it's generated. You can do this with nested loops but if you want to get fancy, you can use something like:
col_sums = map(sum, zip(col_sums, my_randoms))
zip() takes your old sums and your random values and gives you a tuple holding the Nth value of each list stuck together. For example, the first iteration, you'd have col_sums = [0,0,0] and zip(col_sums, my_randoms) would give you [(0,1), (0,1), (0,2)]. mapping sum across that list gives you a new list like [1, 1, 2]. If you put this in every iteration, you'll have a nice sum built up at the end.

How can i do it with list comprehension? [duplicate]

This question already has answers here:
if else in a list comprehension [duplicate]
(8 answers)
What does "list comprehension" and similar mean? How does it work and how can I use it?
(5 answers)
Closed 6 years ago.
I want to make a list comprehension with duplicated values at one loop cycle like this:
s=[]
for i in range(5):
s+=[i,i]
But this doesnt work.
[i, i for i in range(5)]
You can do it like this:
[i for i in range(5) for j in range(2)]
The output is:
[0, 0, 1, 1, 2, 2, 3, 3, 4, 4]
The i loop provides the values, and the j loop serves to repeat those values.
Here is one way with zip:
[i for p in zip(range(5), range(5)) for i in p]
There's no direct way to use a single level list comprehension to do exactly the same thing you're doing with your explicit loop. That's because each iteration of the loop in a comprehension must produce at most one value (it can produce zero if you have an if clause).
However, for this specific case, you can use division to produce the sequence of repeated values you want while iterating on a larger range:
s = [i//2 for i in range(10)]
I think this will do the trick.
[ i/2 for i in range(10)]
List comprehension will have as many elements as the iterator within the comprehension. In this case, the iterator is range(10)
Since, you need 10 elements. You will need to range over 10 and apply some computation( in this case i/2) to get the desired element.
You may try this:
s = [[i, i] for i in range(5)].
And if you want it to be flattened you can try this nested list comprehension.
s = [x for y in [[i, i] for i in range(5)] for x in y]

Explanation and preferred method for appending a list in Python [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I'm learning Python, coming from a C#/Java background, and was playing around with list behavior. I've read some of the documentation, but I don't understand how, or why, slices with indices larger than length-1 make is possible to append items.
ls = ["a", "b", "c", "d"]
n = len(ls) # n = 4
letter = ls[4] # index out of range (list indexes are 0 - 3)
# the following all do the same thing
ls += ["echo"]
ls.append("foxtrot")
ls[len(ls):] = ["golf"] # equivalent to ls[len(ls): len(ls)]
print(ls)
Although it seems odd to me, I understand how slices can modify the list they operate on. What I don't get is why list[len(list)] results in the expected out of bounds error, but list[len(list):] doesn't. I understand that slices are fundamentally different than indexes, just not what happens internally when a slice begins with an index outside of the list values.
Why can I return a slice from a list that starts with a non-existent element (len(list))? And why does this allow me to expand the list?
Also, of the first three above methods for appending an item, which one is preferred from a convention or performance perspective? Are there performance disadvantages to any of them?
While a.append(x) and a[len(a):] = [x] do the same thing, the first is clearer and should almost always be used. Slice replacement may have a place, but you shouldn't look for reasons to do it. If the situation warrants it, it should be obvious when to do it.
Start with the fact that indexing returns an element of the list, and slicing returns a "sublist" of a list. While an element at a particular either exists or does not exist, it is mathematically convenient to think of every list have the empty list as a "sublist" between any two adjacent positions. Consider
>>> a = [1, 2, 3]
>>> a[2:2]
[]
Now consider the following sequence. At each step, we increment the starting index of the slice
>>> a[0:] # A copy of the list
[1, 2, 3]
>>> a[1:]
[2, 3]
>>> a[2:]
[3]
Each time we do so, we remove one more element from the beginning of the list for the resulting slice. So what should happen when the starting index finally equals the final index (which is implicitly the length of the list)?
>>> a[3:]
[]
You get the empty list, which is consistent with the following identities
a + [] = a
a[:k] + a[k:] = a
(The first is a special case of the second where k >= len(a).
Now, once we see why it makes sense for the slice to exist, what does it mean to replace that slice with another list? In other words, what list do we get if we replace the empty list at the end of [1, 2, 3] with the list [4]? You get [1, 2, 3, 4], which is consistent with
[1, 2, 3] + [4] = [1, 2, 3, 4]
Consider another sequence, this time involving slice replacements.
>>> a = [1,2,3]; a[0:] = ['a']; a
['a']
>>> a = [1,2,3]; a[1:] = ['a']; a
[1, 'a']
>>> a = [1,2,3]; a[2:] = ['a']; a
[1, 2, 'a']
>>> a = [1,2,3]; a[3:] = ['a']; a
[1, 2, 3, 'a']
Another way of looking at slice replacement is to treat it as appending a list to a sublist of the original. When that sublist is the list itself, then it is equal to appending to the original.
Why can I return a slice from a list that starts with a non-existent element?
I can't tell for sure, but my guess would be that it's because a reasonable value can be returned (there are no elements this refers to, so return an empty list).
And why does this allow me to expand the list?
Your slice selects the last 0 elements of the list. You then replace those elements with the elements of another list.
Also, of the first three above methods for appending an item, which one is preferred
Use .append() if you're adding a single element, and .extend() if you have elements inside another list.
I'm not sure about performance, but my guess would be that those are pretty well optimized.

Changing a particular element in a 2-D list in python

I have the following code:
a=[[0]*2]*3
print a
a[1][1]=2
print a
the output is:
[[0,0],[0,0],[0,0]]
[[0,2],[0,2],[0,2]]
Why is it changing all the elements of the list?
Shouldn't the output be:
[[0,0],[0,2],[0,0]]
What needs to be done if I just want to change one of the element?
Thanks for the help!
When you do [0] * 2, this results in the list [0, 0]. When you multiply that by 3, you're creating a list with three references to that list. You probably want to do this instead:
a = [[0] * 2 for _ in range(3)]
I've seen this problem many times and it is frustrating.
When you do this:
a = [[0] * 2] * 3
You create a list [0, 0] and pass its reference thrice to form a. Those cloned lists are just references to an original list object. When you modify one, you modify the original and all the rest get modified as well.
The solution is pretty simple:
a = [[0 for j in range(2)] for i in range(3)]
Instead of passing a list by reference, you create a new one each time.
Why is it changing all the elements of the list?
Because they are all the same element.
What needs to be done if I just want to change one of the element?
You need to make a list that contains three different instances of [0, 0].
Multiplying a list makes a list that refers to the same things multiple times.
Try, for example, [[0] * 2 for _ in range(3)].
The reason there is no problem with [0] * 2 is that the fact that both elements are "the same 0" doesn't matter; 0 is 0, and can't be changed. Writing a[0][0] = 1 replaces the 0, but modifies the [0, 0] containing that 0 (by virtue of replacing the 0).

Categories