1

I know that list aliasing is an issue in Python, but I can't figure out a way around it.

def zeros(A):
    new_mat = A
    for i in range(len(A)):
        for j in range(len(A[i])):
            if A[i][j]==0:
                for b in range(len(A)):
                    new_mat[b][j] = 0
            else:
                new_mat[i][j] = A[i][j]
    return A

Even though I don't change the values of A at all, when I return A, it is still modified:

>>> Matrix = [[1,2,3],[5,0,78],[7,3,45]]
>>> zeros(Matrix)
[[1, 0, 3], [5, 0, 78], [7, 0, 45]]

Is this list aliasing? If so, how do you modify elements of a 2D array without aliasing occurring? Thanks so muhc <3.

makansij
  • 9,303
  • 37
  • 105
  • 183

4 Answers4

3

new_mat = A does not create a new matrix. You have merely given a new name to the object you also knew as A. If it's a list of lists of numbers, you might want to use copy.deepcopy to create a full copy, and if it's a numpy array you can use the copy method.

Yann Vernier
  • 15,414
  • 2
  • 28
  • 26
1
new_mat = A[:]

This creates a copy of the list instead of just referencing it. Give it a try.

[:] just specifies a slice from beginning to end. You could have [1:] and it would be from element 1 to the end, or [1:4] and it would be element 1 to 4, for instance. Check out list slicing regarding that.

Name McChange
  • 2,750
  • 5
  • 27
  • 46
  • 2
    This wouldn't solve the problem if `A` contained more complex objects than integers. In fact in this case it will create a new outer list, but alias the inner lists. You need to deep copy. For slices, you'd need `new_mat = [sl[:] for sl in A]` to copy the inner lists, but you'd still run into aliasing if the values in the inner lists are not immutable. – aruisdante Feb 01 '15 at 23:47
  • You are correct. In that case, the `copy` module is what he needs. – Name McChange Feb 01 '15 at 23:49
1

This might help someone else. just do this.

import copy

def zeros(A):
    new_mat = copy.deepcopy(A)
E_K
  • 2,159
  • 23
  • 39
1

QUICK EXPLANATION

If you want A to be the original matrix your function should be like this. Just use np.copy() function

def zeros(A):
    new_mat = np.copy(A) #JUST COPY IT WITH np.copy() function
    for i in range(len(A)):
        for j in range(len(A[i])):
            if A[i][j]==0:
                for b in range(len(A)):
                    new_mat[b][j] = 0
            else:
                new_mat[i][j] = A[i][j]
    return A

THOROUGH EXPLANATION

Let's say we don't want numpy ndarray a to have aliasing. For example, we want to prevent a to change any of its values when we take (for example) a slice of it, we assign this slice to b and then we modify one element of b. We want to AVOID this:

import numpy as np
a = np.array([[1,2,3],[4,5,6],[7,8,9]])
b = a[0]
b[2] = 50
a
Out[]: 
array([[ 1,  2, 50],
       [ 4,  5,  6],
       [ 7,  8,  9]])

Now you might think that treating a numpy-ndarray object as if it was a list object might solve our problem. But it DOES NOT WORK either:

%reset #delete all previous variables
a = np.array([[1,2,3],[4,5,6],[7,8,9]])
b = a[:][0] #this is how you would have copied a slice of "a" if it was a list
b[2] = 50
a
Out[]: 
array([[ 1,  2, 50], #problem persists
       [ 4,  5,  6],
       [ 7,  8,  9]])

The problem gets solved, in this case, if you think of numpy arrays as different objects than python lists. In order to copy numpy arrays you can't do copy = name_array_to_be_copied[:], but copy = np.copy(name_array_to_be_copied). Therefore, this would solve our aliasing problem:

%reset #delete all previous variables
import numpy as np
a = np.array([[1,2,3],[4,5,6],[7,8,9]])
b = np.copy(a)[0] #this is how you copy numpy ndarrays. Calling np.copy() function
b[2] = 50
a
Out[]: 
array([[1, 2, 3], #problem solved
       [4, 5, 6],
       [7, 8, 9]])

P.S. Watch out with the zeros() function. Even after fixing the aliasing issue, your function does not convert to 0 the columns in the new_matrix who have at least one zero in the same column for the matrix A (this is what I think you wanted to acomplish by seeing the incorrectly reported output of your function [[1, 0, 3], [5, 0, 78], [7, 0, 45]], since it actually yields [[1,0,3],[5,0,78],[7,3,45]]). If you want that you can try this:

def zeros_2(A):
    new_mat = np.copy(A)
    for i in range(len(A[0])): #I assume each row has same length.
        if 0 in new_mat[:,i]:
            new_mat[:,i] = 0
    print(new_mat)
    return A
blackcub3s
  • 189
  • 1
  • 4