0

I have a problem with slicing an array. The problem is that if I do some operation inside of function and return modified array my array outside of function is also changed. I could not really understand that behavior of numpy slicing opration on array.

Here is the code:

import numpy as np

def do_smth(a):

    x = np.concatenate((a[..., 1], a[..., 3]))
    y = np.concatenate((a[..., 2], a[..., 4]))
    xmin, xmax = np.floor(min(x)), np.ceil(max(x))
    ymin, ymax = np.floor(min(y)), np.ceil(max(y))

    a[..., 1:3] = a[...,1:3] - np.array([xmin, ymin])
    a[..., 3:5] = a[...,3:5] - np.array([xmin, ymin])
    return a

def main():
        old_a = np.array([[ 0,  1,  2,  3,  4],
                          [ 5,  6,  7,  8,  9],
                          [10, 11, 12, 13, 14]])

       new_a = do_smth(old_a)
       print "new_a:\n", new_a, '\n\n'
       print "old_a:\n", old_a

gives output:

new_a:
[[ 0  0  0  2  2]
 [ 5  5  5  7  7]
 [10 10 10 12 12]]

old_a:
[[ 0  0  0  2  2]
 [ 5  5  5  7  7]
 [10 10 10 12 12]]

Can anyone tell why old_a has been changed? And how can I make old_a unchanged? Thank you

3
  • 2
    What is a in this line new_a = do_smth(a)? If it is old_a what you are seeing is expected. Because when you pass old_a you are passing the reference to the outer list and not the copy. Commented Aug 22, 2014 at 21:24
  • old_a changes because the same object is referenced inside and outside the function. To avoid it, pass a copy. Commented Aug 22, 2014 at 21:25
  • 1
    try new_a=do_smth(a.copy()) Commented Aug 22, 2014 at 21:27

2 Answers 2

2

The problem is not slicing the array, but the fact that numpy arrays are mutable objects.

This means that, whenever you make something like

import numpy as np

a = np.array([1,2,3,4])
b = a

the object b is a view of array a, more precisely, b is just another name for a.

Note that, when you pass a mutable python object (like a python list or dict) to a function that do some operation that changes this object, the original object passed is also changed. This is how python (not only numpy) treats mutable objects.

to get the feeling you can do something like this:

from __future__ import print_funtion
import numpy as np

a = np.ones(3)
b = a

c = [1,1,1]
d = c

print("a before:", a) 
print("b before:", b)
print("c before:", c) 
print("c before:", d)
def change_it(x):
    x[0] = 10

change_it(a)
change_it(c)
print("a after:", a) 
print("b after:", b)
print("c after:", c) 
print("c after:", d)

which gives:

a before: [ 1.  1.  1.]
b before: [ 1.  1.  1.]
c before: [1, 1, 1]
d before: [1, 1, 1]
a after: [ 10.   1.   1.]
b after: [ 10.   1.   1.]
c after: [10, 1, 1]
d after: [10, 1, 1]

Note that the function change_it doesn't even return anything and was only used on a and c but also changed b and d

If you want to avoid this, you must use an explicit copy of your array.

That is easily done by:

def do_smth(original_a):  # change the argument name

    a = original_a.copy()  # make an explicit copy
    x = np.concatenate((a[..., 1], a[..., 3]))
    y = np.concatenate((a[..., 2], a[..., 4]))
    xmin, xmax = np.floor(min(x)), np.ceil(max(x))
    ymin, ymax = np.floor(min(y)), np.ceil(max(y))

    a[..., 1:3] = a[...,1:3] - np.array([xmin, ymin])
    a[..., 3:5] = a[...,3:5] - np.array([xmin, ymin])
    return a
Sign up to request clarification or add additional context in comments.

Comments

2

Although values in Python are passed by assignment, the object you are sending in is actually a reference to a memory location. So thus, when you change the object in the function you are altering the same variable you sent into the function.

You will want to clone the object first, and then do your operations on the copy.

5 Comments

Thanks. Yes, but this is confusing. Each time using copy() is not a way of doing professional programming (I think so). Is there any equivalent procedure on that?
I can't think of a way of having two unique copies of the same data without cloning it...so really this isn't going to have any more memory overhead than what he expected it to do. Also, how would cloning the object be non-professional?
I agree with copy()/clone something. Here, the problem is that some changes inside of the function causes some changes outside which should not behave like that. My point was it is not the case (I think so). Because, imagine that I do huge process with various functions in which I can do anything I want, but outside I do something very different. Here, the problem comes... I can not put something like copy() everytime.. That's what I wanted to say by "not professional". What would you think?
Although I understand your reluctance to want to clone the object every time (which can be done through the constructor of most collections), this is just the way it is unfortunately. Like Java, Python sends all it's objects to functions by value. For objects (i.e. anything other than primitive data types like int, char, float, double, boolean, etc) the "value" of the object is it's location in memory. So that value is a pointer, and unfortunately the value will be modified in the calling function too.
However, if you were to use an immutable type, or modify the slicing algorithm to make a copy of the object being sliced and then return the modified copy from the function copy, leaving the original unscathed. Or better yet, make the slicing algorithm build a new list from the values as it cuts pieces out of it. It would be less memory efficient, but then it wouldn't modify the original object. I'm sure you could extend the class this guy is using and override the slice function with your own. Heck, just override it, create a copy, and then call the base class's slice function on it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.