Unexpected behavior in nested for loop in python - python

Very simple code nested example:
All the code does is create a list of lists that is initialized to zero.
It iterates through the list rows and columns and each position is given a value.
For some reason the last row of the 2D list is duplicated for each row when the final vector is printed.
Number_of_channels=2
Coefficients_per_channel=3
coefficient_array=[[0]*Coefficients_per_channel]*Number_of_channels
print coefficient_array
for channel in range(Number_of_channels):
for coeff in range(Coefficients_per_channel):
coefficient_array[channel][coeff]=coeff*channel
print coefficient_array[channel][coeff]
print coefficient_array
Output:
[[0, 0, 0], [0, 0, 0]]
0
0
0
0
1
2
[[0, 1, 2], [0, 1, 2]]
I actually expect:
[[0, 0, 0], [0, 1, 2]]
Anyone have any idea how come this is happening?

You only duplicate the outer list, but the values of that list are left untouched. Thus, all (both) outer lists contain references to the same inner, mutable list.
>>> example = [[1, 2, 3]]
>>> example *= 2
>>> example
[[1, 2, 3], [1, 2, 3]]
>>> example[0][0] = 5
[[5, 2, 3], [5, 2, 3]]
>>> example[0] is example[1]
True
Better create the inner lists in a loop:
coefficient_array=[[0]*Coefficients_per_channel for i in xrange(Number_of_channels)]
or, illustrated with the python prompt again:
>>> example = [[i, i, i] for i in xrange(2)]
>>> example
[[0, 0, 0], [1, 1, 1]]
>>> example[0][0] = 5
>>> example
[[5, 0, 0], [1, 1, 1]]
>>> example[0] is example[1]
False

With
coefficient_array=[[0]*Coefficients_per_channel]*Number_of_channels
you do a duplication of references to the same object:
coefficient_array[0] is coefficient_array[1]
evaluates to True.
Instead, build your array with
[[coeff*channel for coeff in range(Coefficients_per_channel)] for channel in range(Number_of_channels)]

Try this instead:
coefficient_array=[0]*Number_of_channels
print coefficient_array
for channel in range(Number_of_channels):
coefficient_array[channel] = [0] * Coefficients_per_channel
for coeff in range(Coefficients_per_channel):
coefficient_array[channel][coeff]=coeff*channel
print (channel, coeff)
print coefficient_array[channel][coeff]
print coefficient_array

Related

Python - Flatten lists of lists of two different types in one function

As input, I receive two types of lists of lists made of x and y coordinates that represent polygon and multipolygon geometries. In fact the input is represented in the GeoJson standard
list1 represents coordinates of a simple polygon geometry and list2 represent a multipolygon geometry:
list1 = [[[0 , 0], [0, 1], [0 ,2]]]
list2 = [[[[0, 0] , [0, 1], [0, 2]], [[1, 0], [1, 1], [1 ,2]]]]
Multipolygon geometry (list2) are represented by a list of lists one level deeper than simple polygon geometry (list1).
I want to flatten those lists in order to get those output:
if input is list1 type : list1_out = [[0, 0, 0, 1, 0, 2]]
if input is list2 type : list2_out = [[0, 0, 0, 1, 0, 2], [1, 0, 1, 1, 1, 2]]
I am using the following code that is usually used to flatten lists where input can be a list of the two types:
[coords for polygon in input for coords in polygon]
With this code above, the output for list1 is correct but the output of list2 is the following:
[[[0, 0] ,[0, 1], [0, 2]], [1, 0], [1, 1], [1, 2]]]
Is there a function that could deeply flatten those two types of lists to get the expected output?
Edit: Performance really matter here as the lists are really big
Edit 2: I can use a if sentence to filter each type of list
Try;
for list1
[sum(x, []) for x in list1]
for list2
[sum(x, []) for a in list2 for x in a]
Demo
>>> list1 = [[[0 , 0], [0, 1], [0 ,2]]]
>>> list2 = [[[[0, 0] , [0, 1], [0, 2]], [[1, 0], [1, 1], [1 ,2]]]]
>>> [sum(x, []) for x in list1]
[[0, 0, 0, 1, 0, 2]]
>>> [sum(x, []) for a in list2 for x in a]
[[0, 0, 0, 1, 0, 2], [1, 0, 1, 1, 1, 2]]
>>>
Casting your data to numpy.array, you can use reshape:
import numpy as np
t = np.array([[[[0, 0] , [0, 1], [0, 2]], [[1, 0], [1, 1], [1 ,2]]]])
print t.shape # (1, 2, 3, 2)
t = np.reshape([1, 2, 6]) # merging the last 2 coordinates/axes
flattens the second list as you want.
A code which works for both list (since in both cases you want to merge the last to axis) is:
t = np.array(yourList)
newShape = t.shape[:-2] + (t.shape[-2] * t.shape[-1], ) # this assumes your
# arrays are always at least 2 dimensional (no need to flatten them otherwise...)
t = t.reshape(t, newShape)
The key thing is to keep the shape unchanged up to the last 2 axes (hence
t.shape[:-2]), but to merge the two last axes together (using an axis of length t.shape[-2] * t.shape[-1])
We are creating the new shape by concatenation of these two tuples (hence the extra comma after the multiplication).
Edit: np.reshape() doc is here. The important parameters are the input array (your list, cast as an array), and a tuple which I've called newShape, which represents the lengths along the new axes.

Choose certain items iterating through a list slice python

I have a list
[[0, 1], [1, 0], [0, 2], [1, 1], [2, 0], [0, 3], [1, 2], [2, 1], [3, 0]]
I would like to select the indices from the list that meet the following conditions:
1) The sum of the elements is equal to 3
2) The answer to the above sum does not appear in the list
The way I have thought to do this so far : create a new list with the sums of the above list, countersum, and take the indices, idx of those that meet the condition of being equal to 3, and then:
selection=[n for n, x in list[idx[0]:] if sum not in x]
So idx[0] should contain the index of the first element in the original list meeting the sum condition,
However this is giving me error and I have no idea why!
TypeError: argument of type 'int' is not iterable
Any help greatly appreciated!!
Here's a simple list comprehension:
>>> L = [[0, 1], [1, 0], [0, 2], [1, 1], [2, 0], [0, 3], [1, 2], [2, 1], [3, 0]]
>>> [i for i, j in enumerate(L) if sum(j) == 3 and 3 not in j]
[6, 7]
If I understand correctly, what you were trying to do was something like this:
[n for n, x in an_integer] # an_integer being an integer because that is what list[idx[0]:] returned
And you can't iterate through an integer, hence the error.

Lists of lists: why does changing value at a[1][0] also change a[0][0]? [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 9 years ago.
Can anyone please explain what is happening here? I understand python ints are assigned by value (and not by reference).
>>> alist = [1,2,3]
>>> a = [[0]*3]*4
>>> a[0][0] = len(alist)
>>> alist.append(1)
>>> a[1][0] = len(alist)
>>> a[0][0]a
4
>>>
When you create a list using the * notation, you're repeating the same element. Not copies of the element, but the same exact object.
When the object you're repeating is immutable, such as an integer, you won't notice.
When the object is mutable, such as a list, you get the same list repeated. If you make a change to one list you're making it to all of them.
In your case you made a change to element [1] and it was reflected in element [0].
The problem is that your outer list contains multiple copies of the same inner list value. Here's how you can tell:
a = [[0]*3]*4
print(a[0] is a[1]) # prints True
If you want a two dimensional list of zeros, I suggest using a list comprehension for the outer part so that you get separate inner lists instances:
a = [[0]*3 for _ in range(4)]
print(a[0] is a[1]) # prints False
a[0][0]=1
print(a[1][0]) # prints 0
This might help (just dumping the values of list and a at each line):
#With some commentary
>>> alist = [1,2,3] #Created a list with 3 elements
>>> alist
[1, 2, 3]
>>> a = [[0]*3]*4 #Created a list with 3 copies of one element and then copied it 4 times
>>> a
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> a[0][0] = len(alist) #Set the 0th element of the 0th copy to 3
>>> a #Now you can see where that value was copied
[[3, 0, 0], [3, 0, 0], [3, 0, 0], [3, 0, 0]]
#The rest is just a permutation of that first 'bug'
>>> alist.append(1)
>>> alist
[1, 2, 3, 1]
>>> a[1][0] = len(alist)
>>> a
[[4, 0, 0], [4, 0, 0], [4, 0, 0], [4, 0, 0]]
>>> a[0][0]
4
An unintuitive thing about this code might be that [0]*3 seems do do what you expected while [[0]*3]*4 did not. For this I can only guess (not a Python guru yet) that a single length list of ints was handled in a way that seemed intuitive, while a list of lists fell back to a copy approach.
No problem. [[0]*3]*4 created a list with 4 copies of a list of 3 elements. a[0], a[1], a[2] and a[3] all point to the same list.
>>> a=[[0]*3]*4
>>> a
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> a[0][0]='hello'
>>> a
[['hello', 0, 0], ['hello', 0, 0], ['hello', 0, 0], ['hello', 0, 0]]
>>> for mylist in a:
... print id(a)
...
42701168
42701168
42701168
42701168
>>>

Nested List Indices [duplicate]

This question already has answers here:
List of lists changes reflected across sublists unexpectedly
(17 answers)
Closed 9 years ago.
I have experienced some problem by using a nested list in Python in the code shown bleow.
Basically, I have a 2D list contains all 0 values, I want to update the list value in a loop.
However, Python does not produce the result I want. Is there something that I misunderstand about range() and Python list indices?
some_list = 4 * [(4 * [0])]
for i in range(3):
for j in range(3):
some_list[i+1][j+1] = 1
for i in range(4):
print(some_list[i])
The results I expected are:
[0, 0, 0, 0]
[0, 1, 1, 1]
[0, 1, 1, 1]
[0, 1, 1, 1]
But the actual results from Python are:
[0, 1, 1, 1]
[0, 1, 1, 1]
[0, 1, 1, 1]
[0, 1, 1, 1]
What's going on here?
The problem is caused by the fact that python chooses to pass lists around by reference.
Normally variables are passed "by value", so they operate independently:
>>> a = 1
>>> b = a
>>> a = 2
>>> print b
1
But since lists might get pretty large, rather than shifting the whole list around memory, Python chooses to just use a reference ('pointer' in C terms). If you assign one to another variable, you assign just the reference to it. This means that you can have two variables pointing to the same list in memory:
>>> a = [1]
>>> b = a
>>> a[0] = 2
>>> print b
[2]
So, in your first line of code you have 4 * [0]. Now [0] is a pointer to the value 0 in memory, and when you multiply it, you get four pointers to the same place in memory. BUT when you change one of the values then Python knows that the pointer needs to change to point to the new value:
>>> a = 4 * [0]
>>> a
[0, 0, 0, 0]
>>> [id(v) for v in a]
[33302480, 33302480, 33302480, 33302480]
>>> a[0] = 1
>>> a
[1, 0, 0, 0]
The problem comes when you multiply this list - you get four copies of the list pointer. Now when you change one of the values in one list, all four change together:
>>> a[0][0] = 1
>>> a
[[1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0]]
The solution is to avoid the second multiplication. A loop does the job:
>>> some_list = [(4 * [0]) for _ in range(4)]
Actually all the objects in your list are same, so changing one changes others too:
In [151]: some_list = 4 * [(4 * [0])]
In [152]: [id(x) for x in some_list]
Out[152]: [148641452, 148641452, 148641452, 148641452]
In [160]: some_list[0][1]=5 #you think you changed the list at index 0 here
In [161]: some_list
Out[161]: [[0, 5, 0, 0], [0, 5, 0, 0], [0, 5, 0, 0], [0, 5, 0, 0]] #but all lists are changed
Create your list this way:
In [156]: some_list=[[0]*4 for _ in range(4)]
In [157]: some_list
Out[157]: [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
In [158]: [id(x) for x in some_list]
Out[158]: [148255436, 148695180, 148258380, 148255852]
In [163]: some_list[0][1]=5
In [164]: some_list
Out[164]: [[0, 5, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] #works fine in this case

Python Two-Dimensional Query..

I am seeing a very unusual behavior in python.. Kindly let me know what am i doing wrong!!
bc = [[0]*(n+1)]*(n+1)
for i in range(n+1):
bc[i][i] = 1
print (bc)
Output
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
I am trying to initialize the diagonal elements of two dimensional array to 1, but it is initializing all the elements with 1. I think I am doing something wrong with accessing two dimensional Array..
Also, kindly let me know how can I use two loops to access all the elements of two dimensional array.. my next step..
Thanks.
Your array is initialized incorrectly. The correct way to initialize a 2d array is this:
bc = [[0 for i in xrange(n + 1)] for i in xrange(n + 1)]
It's a common mistake, but the * operator copies the pointer to an list rather than copying the list, so while it looks like you have a 2d list, you actually have a 1d list of pointers to the same list.
the problem is that each array in your array is the same array in memory. you need a new array each time. the [[0]]*6 will for example make 6 of the same arrays in an array, editing one of them will update the other ones.
e.g.
>>> x=[1]
>>> y=x
>>> x.append(3)
>>> x
[1, 3]
>>> y
[1, 3]
>>> z=[x]*3
>>> x.append(6)
>>> z
[[1, 3, 4, 6], [1, 3, 4, 6], [1, 3, 4, 6]]
here is a fix by simply editing bc to be n+1 different arrays:
n=4
bc = [[0]*(n+1) for i in range(n+1)]
for i in range(n+1):
bc[i][i] = 1
print (bc)
[[1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, 0], [0, 0, 0, 0, 1]]
Try this one:
bc = [[0 for i in range(n+1)] for j in range(n+1)]
In your example you have only one (!) instance of [0] which is referenced multiple times. So if you change that instance, all references are changed.

Categories

Resources