Search code examples
pythondictionaryadjacency-matrix

Fill a dummy matrix with help of dictionary keys


I want to use dictionary keys to get to their respective values and then use these values to reference certain elements in my 2D Array.

I do have a 2d-dummy matrix which I create like this:

self.matrix = [[0] * self.length] * self.length

which creates an N x N matrix depending on the length

I also have a sorted list of nodes that have names (=keys) and I want to map these names to indices (=values) 0..N

self.data = dict(zip(self.nodes, self.index_array))

It all works perfectly fine up until I try to fill my dummy adjacency matrix with "1" for Ni is connected to Nj. "edges" is a list of tuples: edges = [("u1","v1"),("u1","v2"),...,("ui","uj")]

for row in edges:
    self.matrix[self.data[row[0]]][self.data[row[1]]] = 1

Now when I run the method above, I get a matrix that is full of ones when there should only be ones for every connection between node u and node v


I tried modelling this problem in a smaller manner and here it worked perfectly! I don't know what's going on.

a = {"3": 0, "4": 1, "5": 2}
edges = [("3", "5"), ("4", "3"), ("5", "3")]
nodes = ["3", "4", "5"]
index = [0, 1, 2]

data = dict(zip(nodes, index))

matrix = [[0, 0, 0],
          [0, 0, 0],
          [0, 0, 0]]

for row in edges:
    matrix[data[row[0]]][data[row[1]]] = 1

print(a)
print(data)
print(matrix)

Solution

  • This does not create the matrix correctly:

    self.matrix = [[0] * self.length] * self.length
    

    Use:

    self.matrix = [[0] * self.length for _ in range(self.length)]
    

    The reason is multiplying a list creates multiples of the references in the list, so every row is a reference to the same list in your original code.

    Here's an example of the difference:

    >>> A = [[0] * 3] * 3
    >>> A
    [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
    >>> A [0][0] = 1
    >>> A
    [[1, 0, 0], [1, 0, 0], [1, 0, 0]]
    

    Note above how all three rows changed. This is due to each row containing a copy of the same list:

    >>> A = [[0] * 3 for _ in range(3)]
    >>> A
    [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
    >>> A[0][0] = 1
    >>> A
    [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
    

    Now a new row of three zeroes is created for each row. Changing one element in a row doesn't change all the rows.

    Note that [0] * 3 also duplicates the references to the integer zero as well. Being an immutable object this isn't a problem, but if you have a mutable object you don't want three copies. You would use [mutable_obj() for _ in range(3)] to create 3 different objects, so if you edit one the others don't change.