Is there aliasing in a 2-D array in Python? - python

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.

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.

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.

This might help someone else. just do this.
import copy
def zeros(A):
new_mat = copy.deepcopy(A)

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

Related

Fanccy Indexing vs View in Numpy part II

Fancy Indexing vs Views in Numpy
In an answer to this equation: is is explained that different idioms will produce different results.
Using the idiom where fancy indexing is to chose the values and said values are set to a new value in the same line means that the values in the original object will be changed in place.
However the final example below:
https://scipy-cookbook.readthedocs.io/items/ViewsVsCopies.html
"A final exercise"
The example appears to use the same idiom:
a[x, :][:, y] = 100
but it still produces a different result depending on whether x is a slice or a fancy index (see below):
a = np.arange(12).reshape(3,4)
ifancy = [0,2]
islice = slice(0,3,2)
a[islice, :][:, ifancy] = 100
a
#array([[100, 1, 100, 3],
# [ 4, 5, 6, 7],
# [100, 9, 100, 11]])
a = np.arange(12).reshape(3,4)
ifancy = [0,2]
islice = slice(0,3,2)
a[ifancy, :][:, islice] = 100 # note that ifancy and islice are interchanged here
>>> a
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
My intuition is that if the first set of fancy indexes is a slice it treats the object like a view and therefore the values in the orignal object are changed.
Whereas in the second case the first set of fancy indexes is itself a fancy index so it treats the object as a fancy index creating a copy of the original object. This then means that the original object is not changed when the values of the copy object are changed.
Is my intuition correct?
The example hints that one should think of the sqeuence of getitem and setitem can someone explain it to my properly in theis way?
Python evaluates each set of [] separately. a[x, :][:, y] = 100 is 2 operations.
temp = a[x,:] # getitem step
temp[:,y] = 100 # setitem step
Whether the 2nd line ends up modifying a depends on whether temp is a view or copy.
Remember, numpy is an addon to Python. It does not modify basic Python syntax or interpretation.

Python: Complex for-loops

I am working through some code trying to understand some Python mechanics, which I just do not get. I guess it is pretty simple and I also now, what it does, but i do not know how it works. I understand the normal use of for-loops but this here... I do not know.
Remark: I know some Python, but I am not an expert.
np.array([[[S[i,j]] for i in range(order+1)] for j in range(order+1)])
The second piece of code, I have problems with is this one:
for i in range(len(u)):
for j in range(len(v)):
tmp+=[rm[i,j][k]*someFuction(name,u[i],v[j])[k] for k in range(len(rm[i,j])) if rm[i,j][k]]
How does the innermost for-loop work? And also what does the if do here?
Thank you for your help.
EDIT: Sorry that the code is so unreadable, I just try to understand it myself. S, rm are numpy matrices, someFunction returns an array with scalar entries, andtmp is just a help variable
There are quite a few different concepts inside your code. Let's start with the most basic ones. Python lists and numpy arrays have different methodologies for indexation. Also you can build a numpy array by providing it a list:
S_list = [[1,2,3], [4,5,6], [7,8,9]]
S_array = np.array(S_list)
print(S_list)
print(S_array)
print(S_list[0][2]) # indexing element 2 from list 0
print(S_array[0,2]) # indexing element at position 0,2 of 2-dimensional array
This results in:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[[1 2 3]
[4 5 6]
[7 8 9]]
3
3
So for your first line of code:
np.array([[[S[i,j]] for i in range(order+1)] for j in range(order+1)])
You are building a numpy array by providing it a list. This list is being built with the concept of list comprehension. So the code inside the np.array(...) method:
[[[S[i,j]] for i in range(order+1)] for j in range(order+1)]
... is equivalent to:
order = 2
full_list = []
for j in range(order+1):
local_list = []
for i in range(order+1):
local_list.append(S_array[i, j])
full_list.append(local_list)
print(full_list)
This results in:
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
As for your second snippet its important to notice that although typically numpy arrays have very specific and constant (for all the array) cell types you can actually give the data type object to a numpy array. So creating a 2-dimensional array of lists is possible. It is also possible to create a 3-dimensional array. Both are compatible with the indexation rm[i,j][k]. You can check this in the following example:
rm = np.array(["A", 3, [1,2,3]], dtype="object")
print(rm, rm[2][0]) # Acessing element 0 of list at position 2 of the array
rm2 = np.zeros((3, 3, 3))
print(rm2[0, 1][2]) # This is also valid
The following code:
[rm[i,j][k]*someFuction(name,u[i],v[j])[k] for k in range(len(rm[i,j])) if rm[i,j][k]]
... could be written as such:
some_list = []
for k in range(len(rm[i,j])):
if rm[i, j][k]: # Expecting a boolean value (or comparable)
a_list = rm[i,j][k]*someFuction(name,u[i],v[j])
some_list.append(a_list[k])
The final detail is the tmp+=some_list. When you sum two list they'll be concatenated as can been seen in this simple example:
tmp = []
tmp += [1, 2, 3]
print(tmp)
tmp += [4, 5, 6]
print(tmp)
Which results in this:
[1, 2, 3]
[1, 2, 3, 4, 5, 6]
Also notice that multiplying a list by a number will effectively be the same as summing the list several times. So 2*[1,2] will result in [1,2,1,2].
Its a list comprehension, albeit a pretty unreadable one. That was someome doing something very 'pythonic' in spite of readablity. Just look up list comprehensions and try to rewrite it yourself as a traditional for loop. list comprehensions are very useful, not sure I would have gone that route here.
The syntax for a list comprehension is
[var for var in iterable if optional condition]
So this bottom line can be rewritten like so:
for k in range(len(rm[i,j]):
if rm[i,j][k]:
tmp+= rm[i,j][k]*someFunction(name,u[i],v[j])[k]

How to properly remove elements from array in loop using Python

I tried to remove only one element from an array and print remaining ones in a loop:
arr = [1,2,3,4,5]
for i in arr:
a = arr
a.remove(i)
print a
So I am expecting it to print this:
[2, 3, 4, 5]
[1, 3, 4, 5]
[1, 2, 3, 5]
[1, 2, 3, 4]
Why am I gettting the following results instead:
[2, 3, 4, 5]
[2, 4, 5]
[2, 4]
This is a classic problem of deep vs shallow copy.
Python copies the array by reference. So, any changes to the new variable (a in your case) will be reflected in the main array (arr). Removing element from a also remove elements from arr.
You need to use following for creating a copy.
a = arr[:]
This will not remove any elements from arr.
You've already got explanations, I'll provide an alternative - you can produce requested output without using remove at all:
arr = [1,2,3,4,5]
for i in arr:
a = [x for x in arr if x != i]
print a
(beware of non-unique elements in arr, if there are any, both this code and yours will produce unexpected results, though in different ways).
You can try this.
arr = [1, 2, 3, 4, 5, 6, 7 ]
for index, item in enumerate(arr):
print(arr[0:index] + arr[index+1:])
I think this can help you.
If you don't have any repeated value you can use this too.
for item in arr:
print(set(arr) - set([item]))
Python lists are mutable i.e. modifying operations (like list.remove()) on them do not produce a new list but modify the existing list instead so each your cycle actually permanently modifies the source list and elements are lost. You'd need to copy your whole list each time you want to modify it in order to achieve what you want, or you can create a new list with elements excluded, or, for very long lists, reconstructing by slicing is probably the most performant way:
arr = [1,2,3,4,5]
for i in range(len(arr)):
a = arr[0:i] + arr[i+1:]
print(a)

Problems creating a mozaic with a list of numpy arrays

So, I'm trying to do this image processing exercise. I have to separate a 512px image in 16 equal size block, mixing them up in a pre arranged configuration, creating a **mozaic**. For this I'm using the **numpy** library and running some **tests** to see how it works. That said, I have this code:
import numpy as np
from scipy import misc
from scipy import ndimage
import matplotlib.pyplot as plt
img = misc.imread("test.png")
list = [(img[0:128, 0:128]), (img[0:128, 128:256]), (img[0:128, 256:384]), (img[0:128, 384:512]),
(img[128:256, 0:128]), (img[128:256, 128:256]), (img[128:256, 256:384]), (img[128:256, 384:256]),
(img[256:384, 0:128]), (img[256:384, 128:256]), (img[256:384, 256:384]), (img[256:384, 384:512]),
(img[384:512, 0:128]), (img[384:512, 128:256]), (img[384:512, 256:384]), (img[384:512, 384:512])]
list[0] = list[1]
print list[0], '\n\n\n', list[1]
plt.imshow(img)
plt.show()
As you can see, I separated the image with the index and put it all in the list. Then I'm trying to replace one "block" for other with this:
list[0] = list[1]
The problem is when I print them
print list[0], '\n\n\n', list[1]
they'are equal, but if I do
print img[0:128, 0:128],'\n\n', img[0:128, 128:256]
I can see that the real array wasn't changed at all. **How can I make the canges apply to the image array and not just to the list?** Or there is a simple way to do this? I took a look in the split functions of numpy and none of them seemed to help here.
PS: I know this works, but I need to do with the list.
img[0:128, 0:128] = list[1]
So you have a list of views of img (the () are not needed)
alist = [img[0:128, 0:128], img[0:128, 128:256], img[0:128, 256:384], ...]
Doing
alist[0] = alist[1]
amounts to changing the list to:
alist = [img[0:128, 128:256], img[0:128, 128:256], img[0:128, 256:384], ...]
because the action just changes the pointer in alist[0] to the pointer in alist[1]. Now the first 2 elements are same thing.
alist[0][...] = alist[1]
would change the contents of alist[0] array without changing the object that it points to. In other words img[0:128, 128:256] is a mutable object, and img[0:128, 128:256][...]=... changes the contents (if done correctly).
This kind of action is a little tricky. You have to match dimensions. I first wrote alist[0][:] = alist[1] and then realized the alist[0] is a 2d array, so I need to use [:,:] or [...].
Illustrating on a smaller array
In [69]: img=np.arange(16).reshape(4,4)
In [70]: alist=[img[0:2,0:2],img[0:2,2:4],img[2:4,0:2],img[2:4,2:4]]
In [71]: alist[0][...]=alist[1]
In [72]: alist
Out[72]:
[array([[2, 3],
[6, 7]]), array([[2, 3],
[6, 7]]), array([[ 8, 9],
[12, 13]]), array([[10, 11],
[14, 15]])]
In [73]: img
Out[73]:
array([[ 2, 3, 2, 3],
[ 6, 7, 6, 7],
[ 8, 9, 10, 11],
[12, 13, 14, 15]])
I'm not super clear on exactly what you're asking - can you just set
img[0:128, 0:128] = img[128:256, 128:256]
or are you looking for something else?
Also, be careful calling your list list in Python - list is a built-in type in Python and changing it might make things confusing later on down the track.

appending multidimensional elements into numpy arrays without reshaping

I have a few simple questions I'm not able to find the answer to. They are both stated in the following example code. Thank you for any help!
import numpy as np
#here are two arrays to join together
a = np.array([1,2,3,4,5])
b = np.array([6,7,8,9,10])
#here comes the joining step I don't know how to do better
#QUESTION 1: How to form all permutations of two 1D arrays?
temp = np.array([]) #empty array to be filled with values
for aa in a:
for bb in b:
temp = np.append(temp,[aa,bb]) #fill the array
#QUESTION 2: Why do I have to reshape? How can I avoid this?
temp = temp.reshape((int(temp.size/2),2))
edit: made code more minimal
To answer your first question, you can use np.meshgrid to form those combinations between elements of the two input arrays and get to the final version of temp in a vectorized manner avoiding those loops, like so -
np.array(np.meshgrid(a,b)).transpose(2,1,0).reshape(-1,2)
As seen, we would still need a reshape if you intend to get a 2-column output array.
There are other ways we could construct the array with the meshed structure and thus avoid a reshape. One of those ways would be with np.column_stack, as shown below -
r,c = np.meshgrid(a,b)
temp = np.column_stack((r.ravel('F'), c.ravel('F')))
The proper way to build an array iteratively is with list append. np.append is poorly named, and often mis used.
In [274]: a = np.array([1,2,3,4,5])
...: b = np.array([6,7,8,9,10])
...:
In [275]: temp = []
In [276]: for aa in a:
...: for bb in b:
...: temp.append([aa,bb])
...:
In [277]: temp
Out[277]:
[[1, 6],
[1, 7],
[1, 8],
[1, 9],
[1, 10],
[2, 6],
....
[5, 9],
[5, 10]]
In [278]: np.array(temp).shape
Out[278]: (25, 2)
It's better to avoid loops at all, but if you must, use this list append approach.

Categories