How to make a copy of a 2D array in Python? [duplicate] - python

This question already has answers here:
Copying nested lists in Python
(3 answers)
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 4 years ago.
X is a 2D array. I want to have a new variable Y that which has the same value as the array X. Moreover, any further manipulations with Y should not influence the value of the X.
It seems to me so natural to use y = x. But it does not work with arrays. If I do it this way and then changes y, the x will be changed too. I found out that the problem can be solved like that: y = x[:]
But it does not work with 2D array. For example:
x = [[1,2],[3,4]]
y = x[:]
y[0][0]= 1000
print x
returns [ [1000, 2], [3, 4] ]. It also does not help if I replace y=x[:] by y = x[:][:].
Does anybody know what is a proper and simple way to do it?

Using deepcopy() or copy() is a good solution.
For a simple 2D-array case
y = [row[:] for row in x]

Try this:
from copy import copy, deepcopy
y = deepcopy(x)
I'm not sure, maybe copy() is sufficient.

For 2D arrays it's possible use map function:
old_array = [[2, 3], [4, 5]]
# python2.*
new_array = map(list, old_array)
# python3.*
new_array = list(map(list, old_array))

In your case(since you use list of lists) you have to use deepcopy, because 'The difference between shallow and deep copying is only relevant for compound objects (objects that contain other objects, like lists or class instances):
A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.
A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.'
Note that sample below is simply intended to show you an example(don't beat me to much) how deepcopy could be implemented for 1d and 2d arrays:
arr = [[1,2],[3,4]]
deepcopy1d2d = lambda lVals: [x if not isinstance(x, list) else x[:] for x in lVals]
dst = deepcopy1d2d(arr)
dst[1][1]=150
print dst
print arr

I think np.tile also might be useful
>>> a = np.array([0, 1, 2])
>>> np.tile(a, 2)
array([0, 1, 2, 0, 1, 2])
>>> np.tile(a, (2, 2))
array([[0, 1, 2, 0, 1, 2],
[0, 1, 2, 0, 1, 2]])
>>> np.tile(a, (2, 1, 2))
array([[[0, 1, 2, 0, 1, 2]],
[[0, 1, 2, 0, 1, 2]]])

Related

Only if I'm not using __iadd__, I get TypeError: can only concatenate list (not "str") to list [duplicate]

consider the following code:
>>> x = y = [1, 2, 3, 4]
>>> x += [4]
>>> x
[1, 2, 3, 4, 4]
>>> y
[1, 2, 3, 4, 4]
and then consider this:
>>> x = y = [1, 2, 3, 4]
>>> x = x + [4]
>>> x
[1, 2, 3, 4, 4]
>>> y
[1, 2, 3, 4]
Why is there a difference these two?
(And yes, I tried searching for this).
__iadd__ mutates the list, whereas __add__ returns a new list, as demonstrated.
An expression of x += y first tries to call __iadd__ and, failing that, calls __add__ followed an assignment (see Sven's comment for a minor correction). Since list has __iadd__ then it does this little bit of mutation magic.
The first mutates the list, and the second rebinds the name.
1)'+=' calls in-place add i.e iadd method. This method takes two parameters, but makes the change in-place, modifying the contents of the first parameter (i.e x is modified). Since both x and y point to same Pyobject they both are same.
2)Whereas x = x + [4] calls the add mehtod(x.add([4])) and instead of changing or adding values in-place it creates a new list to which a points to now and y still pointing to the old_list.

Adding elements to numpy array

Using NumPy:
X= numpy.zeros(shape=[1, 4], dtype=np.int)
How can I add a list, such as [1,2,3,4]? I tried numpy.add(X,[1,2,3,4]) and np.hstack((1,2,3,4)) but none of them work!
I know how to use that in standard Python list using append method but I want to use numpy for performance.
Numpy arrays don't change shape after they are created. So after invoking method zeros((1,4), ...), you already have a 1x4 matrix full of zeroes. To set its elements to values other than zeroes, you need to use the assignment operator:
X[0] = [1, 2, 3, 4] # does what you are trying to achieve in your question
X[0, :] = [1, 2, 3, 4] # equivalent to the above
X[:] = [1, 2, 3, 4] # same
X[0, 1] = 2 # set the individual element at [0, 1] to 2

Modifying list slice passed into a function

Is it possible to pass a slice of a list into a function and modify the list via the slice?
This doesn't seem to work:
def foo(a_list):
a_list[0]='abc'
x=[1,2,3,4]
foo(x[0:2])
I want x to now be x=['abc',2,3,4]
No. A "list slice" in the sense you describe is nothing but a new list. When you do x[0:2], the resulting list does not "know" that it was created as a slice of another list. It's just a new list.
What you could do is pass in the slice object itself, separately from the list:
def foo(a_list, a_slice):
a_list[a_slice]='abc'
>>> foo(x, slice(0, 2))
>>> x
['a', 'b', 'c', 3, 4]
No! Slices create copies of lists (see the documentation). You can do this:
>>> x = [1, 2, 3, 4]
>>> x[1:3] = [7, 8, 9]
>>> X
[1, 7, 8, 9, 4]
But, when you get a new list from a slicing operation, it's a copy, and thus changes to it won't affect the original:
>>> x = [1, 2, 3, 4]
>>> y = x[1:3]
>>> y[0] = 5
>>> y
[5, 3]
>>> x
[1, 2, 3, 4]
def foo(a_list):
a_list[0]='abc'
return a_list
x=[1,2,3,4]
foo(x) #it returns x=['abc',2,3,4]
Assumably you're trying to pass in x[0:2], rather than x[0,2], but the reason it doesn't work is because when you create a slice you are creating a subarray copy of x.
You are not operating on the same instance of that array, what you are doing is passing an entirely new array. Passing in 'x' alone would work, but passing in x[0:2] would not unless you specifically wrote it as x[0:2] = foo(x[0:2]) and had foo() return a_list.
As brenns10 explained, slices create a copy (even in python 3.0) of your origional data.
You could do something like the following:
def foo(x):
x[0] = 'abc'
return x
x = [0, 1, 2, 3]
x[0:2] = foo(x[0:2]) # x = ['abc', 1, 2, 3]
While this gives you the desired outcome, it doesn't exactly work as you would want. This could be problematic if needing to perform large slices as you'd have to perform a lot of copying.

Python: Why still list elements are not disappeared after using procedure?

I define this function to do: [1,2,3] --> [2,3,1]
def shift_to_left(p):
p.append(p[0])
return p[1:]
When I check like this, results are ok:
p1 = [1,2,3]
print p1
p1 = shift_to_left(p1)
print p1
The result:
[1, 2, 3]
[2, 3, 1]
However, when I introduce another list and concatenate as I go the result is different:
ss = []
p1 = [1,2,3]
ss.append(p1)
p1 = shift_to_left(p1)
ss.append(p1)
print ss
The result
[[1, 2, 3, 1], [2, 3, 1]]
But I want:
[1,2,3]
[2,3,1]
why is it happening?
Thanks very much,
In Python, most arguments are taken by reference.
Your function, shift_to_left, actually mutates its argument (through the use of append), but then returns a slice (which is a shallow copy of the list).
When you replace your original variable with the output of shift_to_left, this behaviour is hidden:
In [1]: def shift_to_left(p):
...: p.append(p[0])
...: return p[1:]
...:
In [2]: xs = [1, 2, 3]
In [3]: xs = shift_to_left(xs)
In [4]: xs
Out[4]: [2, 3, 1]
But if we instead assign the result into a new variable, we can see that the original list has indeed been changed:
In [5]: ys = shift_to_left(xs)
In [6]: ys
Out[6]: [3, 1, 2]
In [7]: xs
Out[7]: [2, 3, 1, 2]
Our result, ys, is the slice of xs from the second element onwards. That's what you expected.
But xs itself has also been changed by the call to append: it's now one element longer than before.
This is what you're experiencing in your second example.
If you do not want this behaviour, one way of avoiding it is by passing a copy of your list to shift_to_left:
In [8]: zs = shift_to_left(ys[:])
In [9]: zs
Out[9]: [1, 2, 3]
In [10]: ys
Out[10]: [3, 1, 2]
Here, you can see that the original list ys has not been modified, as shift_to_left was given a copy of it, not the object itself. (This is still passing by reference, of course; it's just not a reference to ys).
Alternatively, and probably more reasonably, you could change shift_to_left itself, so that it does not modify its arguments:
def shift_to_left(xs):
return xs[1:] + xs[0] # build a new list in the return statement
The big problem with both these approaches is that they create lots of copies of lists, which can be incredibly slow (and use a lot of memory) when the lists are large.
Of course, as #Marcin points out, if this is more than an academic exercise, you should probably use one of the built-in data structures such as deque.
If you want to shift/rotate elements in a list, I think better would be to use a deque, rather than reinvent the wheel. For example:
from collections import deque
d = deque([1,2,3])
d.rotate(-1)
print(d)
#[2, 3, 1]
If you run your code here, you can notice that ss remains pointing to the original (mutated in your shift function because of p.append(p[0])) copy of p1, where as p1 points to a knew list all together when it gets reassigned, resulting in the behavior. (Step 10 out of 11)
(p becomes mutated, and ss[0] = p)
(p1 gets assigned to a new list altogether, which is latter appended to ss)
why is it happening?
return p[1:] is "non-destructive": it creates a new list. However, p.append(p[0]) is "destructive": it changes p itself.
First you append p1 to ss. This makes [[1, 2, 3]], where [1, 2, 3] is p1.
Then you do your shift_to_left, which changes p1 to [1, 2, 3, 1] and returns [2, 3, 1]. Because p1 is contained in ss, your ss becomes [[1, 2, 3, 1]], and then you append the new p1 to form [[1, 2, 3, 1], [2, 3, 1]].
A better implementation would be purely non-destructive:
def shift_to_left(p):
return p[1:] + [p[0]]
Try this:
p1 = [1,2,3]
p1 = shift_to_left(p1)
ss = []
ss.extend(p1)
print ss
That prints [2, 3, 1]. Use extend() instead because append() will create an array in an array. Also you had an extra call to ss.append(p1).

python implementation of list append [duplicate]

consider the following code:
>>> x = y = [1, 2, 3, 4]
>>> x += [4]
>>> x
[1, 2, 3, 4, 4]
>>> y
[1, 2, 3, 4, 4]
and then consider this:
>>> x = y = [1, 2, 3, 4]
>>> x = x + [4]
>>> x
[1, 2, 3, 4, 4]
>>> y
[1, 2, 3, 4]
Why is there a difference these two?
(And yes, I tried searching for this).
__iadd__ mutates the list, whereas __add__ returns a new list, as demonstrated.
An expression of x += y first tries to call __iadd__ and, failing that, calls __add__ followed an assignment (see Sven's comment for a minor correction). Since list has __iadd__ then it does this little bit of mutation magic.
The first mutates the list, and the second rebinds the name.
1)'+=' calls in-place add i.e iadd method. This method takes two parameters, but makes the change in-place, modifying the contents of the first parameter (i.e x is modified). Since both x and y point to same Pyobject they both are same.
2)Whereas x = x + [4] calls the add mehtod(x.add([4])) and instead of changing or adding values in-place it creates a new list to which a points to now and y still pointing to the old_list.

Categories

Resources