2

I'm trying to tranpose a matrix in Python 3.x without using numpy. For some reason, I'm having problems with the assignment of new_matrix[col_num][row_num]. For example, if I create a new instance of Matrix like test = Matrix([[1,2,3,],[4,5,6],[7,8,9]]), the first time it goes through the inner for loop, new_matrix becomes [[1,None,None],[1,None,None],[1,None,None]] instead of [[1,None,None],[None,None,None],[None,None,None]]. I can't figure out why this is happening and why it's assigning a value to ALL the first elements of lists.

class Matrix:
    def __init__(self, matrix):
        self.matrix = matrix

    def transpose(self):
        new_matrix = [[None] * len(self.matrix[0])] * len(self.matrix)
        row_num = 0
        for row_num in range(len(self.matrix)):
            for col_num in range(len(self.matrix[0])):
                print(new_matrix[col_num][row_num])
                print(new_matrix)

                #assignment assigning more than one variable

                new_matrix[col_num][row_num] = self.matrix[row_num][col_num]

                print(new_matrix[col_num][row_num])
                print(new_matrix)

            col_num = 0
        return new_matrix
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
gridproquo
  • 129
  • 10
  • You could use `zip` to transpose. See http://stackoverflow.com/questions/10169919/python-matrix-transpose-and-zip. – kennytm Jan 27 '17 at 20:11

3 Answers3

2

That's because you construct the newmatrix with:

new_matrix = [[None] * len(self.matrix[0])] * len(self.matrix)

If you do:

<array-expr> * number

you do not copy that list, you copy the reference. So that means that when you manipulate newmatrix[0] you also manipulate newmatrix[1], etc.

You should solve it with list-comprehension:

new_matrix = [[None] * len(self.matrix[0]) for _ in range(len(self.matrix))]

Since here you actually construct a new list for each row.

So the algorithm should look:

class Matrix:
    def __init__(self, matrix):
        self.matrix = matrix

    def transpose(self):
        new_matrix = [[None] * len(self.matrix[0]) for _ in range(len(self.matrix))]
        row_num = 0
        for row_num in range(len(self.matrix)):
            for col_num in range(len(self.matrix[0])):
                print(new_matrix[col_num][row_num])
                print(new_matrix)

                #assignment assigning more than one variable

                new_matrix[col_num][row_num] = self.matrix[row_num][col_num]

                print(new_matrix[col_num][row_num])
                print(new_matrix)

            col_num = 0
        return new_matrix
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
2
new_matrix = [[None] * len(self.matrix[0])] * len(self.matrix)

must be replaced by

new_matrix = [[None for  j in self.matrix[0]] for i in self.matrix] 

This way, all the None will be "differents".

but the shorter way is to directly build the result with extension lists :

MT= [[M[j][i] for j in range(len(M))] for i in range(len(M[0]))]
B. M.
  • 18,243
  • 2
  • 35
  • 54
  • You do not need to do this for the inner list, since `None`s are immutable. – Willem Van Onsem Jan 27 '17 at 20:17
  • Just realized that new_matrix should be = [[None for j in self.matrix] for i in self.matrix[0]], not the other way around, because the number of rows in the new matrix should be the number of /columns/ in the original matrix. Other than that, thanks! – gridproquo Jan 27 '17 at 20:33
1

Here's a hint:

In [9]: arr = [[None] * 2] * 3

In [10]: arr
Out[10]: [[None, None], [None, None], [None, None]]

In [11]: arr[1][1] = 2

In [12]: arr
Out[12]: [[None, 2], [None, 2], [None, 2]]

Do you see the problem?

The list replication (* operator) repeats the same nested list object within the containing list. Instead, you should be creating a new list every time.

Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412