I am having trouble writing a setter-function for a Matrix class that is supposed to handle multidimensional matrices for my implementation of a dynamic programming algorithm.
The matrix is stored using a nested list. Since the dimensionality is not known the idea behind the code is to use a coordinate (a list of indices) and iteratively step into the matrix upon reading an index from the coordinate.
def setField(self, coordinate, value):
coordinate = coordinate[::-1] # reverse the coordinate so .pop() can be used
field = self.matrix
while len(coordinate) > 1:
field = field[coordinate.pop()]
field[coordinate[0]] = value
Using the Code:
>>> m = Matrix([3,3,3]) # initialize Matrix of size 3 in all dimensions
>>> m.tolist() # list representation
[[[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]]]
>>> m.setField([1,1,1], 42) # at position [1,1,1] insert 42
>>> m.tolist()
[[[0, 0, 42], [0, 0, 42], [0, 0, 42]], [[0, 0, 42], [0, 0, 42], [0, 0, 42]], [[0, 0, 42], [0, 0, 42], [0, 0, 42]]]
Now the trouble is, that the 'stepping into the matrix' part is done with a reference to the matrix. For reasons I don't understand, the change is not just happening in one field (where the matrix is supposed to be changed to the given value), but occurs in every sublist of the matrix representation.
What confuses me even more, is that the following code works just fine in a Python shell:
>>> m = [[0,0],[0,0]]
>>> field = m
>>> field = field[0]
>>> field[1] = 1
>>> m
[[0, 1], [0, 0]]
What am I doing wrong here?
Why is Python behaving that way?
Edit / Explanation
As pointed out as an Answer, all sublists in the matrix actually are the same.
>>> m = Matrix([2,2])
>>> id(m.matrix[0]) == id(m.matrix[1])
True
To avoid this behaviour, copies have to be used.
The issue you're seeing is because sub-arrays of your matrix are actually the same thing in memory, so modifying one changes them all.
This is what you're doing:
>>> a = [0, 0]
>>> m = [a, a]
>>> m
[[0, 0], [0, 0]]
>>> m[0][1] = 42
>>> m
[[0, 42], [0, 42]]
>>> id(m[0]) == id(m[1]) # same object in memory
True
This is what you want to do:
>>> a = [0,0]
>>> m = [a.copy(), a.copy()]
>>> m[0][1] = 42
>>> m
[[0, 42], [0, 0]]
>>> id(m[0]) == id(m[1]) # different objects in memory
False
That being said, it's a lot easier to do in numpy:
>>> import numpy as np
>>> m = np.zeros((2, 2))
>>> m[0,1] = 42
>>> m
array([[ 0., 42.],
[ 0., 0.]])
Related
Supposed I created the 2D array of 3x4 with zeros initialization.
Then, I want to add 100 into arr[0][1] and I expect that 100 is added Only into arr[0][1] cell. Refer to Case 1 and Case 2:
From Case 1:
what I saw is that 100 is added into every row with column 1 of arr. It is not what i expected.
From Case 2
The result is what i have expected. Can anyone help me explain?
Case 1:
>>> arr = [[0]*4]*3
>>> arr
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> len(arr)
3
>>> len(arr[0])
4
>>> type(arr)
<class 'list'>
>>> arr[0] [1] = 100
>>> arr
[[0, 100, 0, 0], [0, 100, 0, 0], [0, 100, 0, 0]]
Case 2:
>>> arr = [[0]*4 for _ in range(3)]
>>> arr
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> type(arr)
<class 'list'>
>>> arr[0] [1] = 100
>>> arr
[[0, 100, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
[0]*4 creates a list of references to the same object 0. Since integers are immutable, that's OK; changing one element replaces it with a new object. However, [[0]*4]*3] creates a list with 3 references to the same, mutable list object that contains 4 references to 0. Changing an element of one list, as you've seen, shows up as a change in all 3 references.
In the list comprehension, a new list [0]*4 is created for each value of _, rather than simply storing a new reference to the same list.
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
new = zero(rows_A,cols_B)
for i in range(rows_A):
for j in range(cols_B):
new[i][j] += np.sum(A[i] * B[:,j])
If I'm using this form of array [[0, 0, 0], [0, 1, 0], [0, 2, 1]] in B
it is giving me an error
TypeError: list indices must be integers, not tuple
but if I'm using same array B, in place of A, it's working well.
I am getting this type of return array
[[0, 0, 0], [0, 1, 0], [0, 2, 1]]
so i want to convert it into this form
[[0 0 0]
[0 1 0]
[0 2 1]]
numpy.asarray will do that.
import numpy as np
B = np.asarray([[0, 0, 0], [0, 1, 0], [0, 2, 1]])
This produces
array([[0, 0, 0],
[0, 1, 0],
[0, 2, 1]])
which can be indexed with [:, j].
Also, it looks like you're trying to do a matrix product. You can do the same thing with just one line of code using np.dot:
new = np.dot(A, B)
It appears that B is a list. You can't index it as B[:,i] -- Which is implcitly passed to __getitem__ as (slice(None,None,None),i) -- i.e. a tuple.
You could convert B to a numpy array first (B = np.array(B)) and then go from there ...
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Python list problem
I try to initialise a matrix in python.
First I did this:
>>> M=[[0]*4]*4
But here is my probleme, every line is changing when I change the first one:
>>> M
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> M[1][1]=1
>>> M
[[0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0]]
So I did it this way:
>>> M= [ [ 0 for i in range(4) ] for j in range(4) ]
And ut works fine:
>>> M
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> M[1][1]=1
>>> M
[[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
My question is:
What these two expressions really mean ? ans why the first one is behaving this way ?
Thanks in advance for you help.
When you multiply those lists, Python is copying them by reference rather than creating entirely new objects.
A simple example might help, showing what happens with copy by reference:
>>> pie = ['apple', 'cherry', 'pecan']
>>> pie_copy = pie
>>> pie_copy[0] = 'banana'
>>> pie
['banana', 'cherry', 'pecan']
>>> pie is pie_copy
True
>>> new_pie = ['banana', 'cherry', 'pecan']
>>> pie is new_pie
False
In the same way that pie_copy and pie point to the same list, when building lists by multiplying, all the copies point to the same list.
In your second snippet using range() and list comprehensions, you aren't taking a single list and copying it several times; each iteration in the comprehension is creating a new list, so you don't suffer from the same copy by reference problem.
Because here M=[[0]*4]*4
You create a links on objects.
It's the similar as
>>> a = [0, 0, 0]
>>> b = [a,a,a]
>>> b
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> a[1] = 1
>>> b
[[0, 1, 0], [0, 1, 0], [0, 1, 0]]
>>>
UPD links I meant references, sorry if little confusing
Say a is some python object. Then [a] * 4 is equivalent to [a, a, a, a]. What this means depends on whether a is mutable or not. Numbers, strings and tuples are not, so if a is one of these kinds of objects (0 in your example), then you get 4 independently changeable copies. Lists, dictionaries and sets are mutable, and in this case you just get 4 references to the same object, in your case the list [0] * 4. Exploiting this knowledge, you'll see that you can do this:
M = [[0] * 4 for i in range(4)]
and get what you want.
Is there a way in Python to initialize a multi-dimensional array / list without using a loop?
Sure there is a way
arr = eval(`[[0]*5]*10`)
or
arr = eval(("[[0]*5]+"*10)[:-1])
but it's horrible and wasteful, so everyone uses loops (usually list comprehensions) or numpy
Depending on your real needs, the de facto "standard" package Numpy might provide you with exactly what you need.
You can for instance create a multi-dimensional array with
numpy.empty((10, 4, 100)) # 3D array
(initialized with arbitrary values) or create the same arrays with zeros everywhere with
numpy.zeros((10, 4, 100))
Numpy is very fast, for array operations.
The following does not use any special library, nor eval:
arr = [[0]*5 for x in range(6)]
and it doesn't create duplicated references:
>>> arr[1][1] = 2
>>> arr
[[0, 0, 0, 0, 0],
[0, 2, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]]
Sure, you can just do
mylist = [
[1,2,3],
[4,5,6],
[7,8,9]
]
I don't believe it's possible.
You can do something like this:
>>> a = [[0] * 5] * 5
to create a 5x5 matrix, but it is repeated objects (which you don't want). For example:
>>> a[1][2] = 1
[[0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0]]
You almost certainly need to use some kind of loop as in:
[[0 for y in range(5)] for x in range(5)]
Recursion is your friend :D
It's a pretty naive implementation but it works!
dim = [2, 2, 2]
def get_array(level, dimension):
if( level != len(dimension) ):
return [get_array(level+1, dimension) for i in range(dimension[level])]
else:
return 0
print get_array(0, dim)
It depends on what you what to initialize the array to, but sure. You can use a list comprehension to create a 5×3 array, for instance:
>>> [[0 for x in range(3)] for y in range(5)]
[[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
>>> [[3*y+x for x in range(3)] for y in range(5)]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14]]
Yes, I suppose this still has loops—but it's all done in one line, which I presume is the intended meaning of your question?
a = [[]]
a.append([1,2])
a.append([2,3])
Then
>>> a
[[1, 2], [2, 3]]
If you're doing numerical work using Numpy, something like
x = numpy.zeros ((m,n))
x = numpy.ones ((m,n))
Python does not have arrays. It has other sequence types ranging from lists to dictionaries without forgetting sets - the right one depends on your specific needs.
Assuming your "array" is actually a list, and "initialize" means allocate a list of lists of NxM elements, you can (pseudocode):
for N times: for M times: add an element
for N times: add a row of M elements
write the whole thing out
You say you don't want to loop and that rules out the first two points, but why?
You also say you don't want to write the thing down (in response to JacobM), so how would you exactly do that? I don't know of any other way of getting a data structure without either generating it in smaller pieces (looping) or explicitly writing it down - in any programming language.
Also keep in mind that a initialized but empty list is no better than no list, unless you put data into it. And you don't need to initialize it before putting data...
If this isn't a theoretical exercise, you're probably asking the wrong question. I suggest that you explain what do you need to do with that array.
You can do by this way:
First without using any loop:
[[0] * n] * m
Secondly using simple inline list comprehension:
[[0 for column in range(n)] for row in range(m)]
You can use N-dimensional array (ndarray). Here is the link to the documentation. http://docs.scipy.org/doc/numpy/reference/arrays.ndarray.html