Swapping elements in a copy of a numpy array
When the number of components in each element are not equal, you are having a list of arrays (nested object).
When the number of components are equal, then you have a two dimensional array (one single object).
The copy
you are using is called shallow copy which copies only the top level object (the 2d array in second case, but only the addresses to the arrays in the first case). So in the first case your original data also are changed.
You should use the copy
module:
https://docs.python.org/3/library/copy.html
Swapping two integers in numpy array leads to IndexError
>>> import numpy as np
>>> i = np.array([1, 0, 1, 0, 0, 3, 0, 3])
>>> i
array([1, 0, 1, 0, 0, 3, 0, 3])
>>> a, b = i ==3, i == 1 # save the indices
>>> i[a], i[b] = 1, 3
>>> i
array([3, 0, 3, 0, 0, 1, 0, 1])
How do I swap elements of rows of a Nx2 numpy array if a condition is satisfied?
And here's one using Numpy's boolean indexing directly:
import numpy as np
my_array = np.asarray([[0, 1],
[2, 3],
[4, 5],
[6, 7],
[8, 9]])
swap = np.array([0, 0, 1, 1, 0], dtype=bool)
my_array[swap, :] = my_array[swap,:][:,(1,0)]
Breaking down the key line:
my_array[swap, :] =
means "Assign to the rows whereswap
is true"my_array[swap,:]
means "select the whole row whereswap
is true"[:,(1,0)]
means "for each row of what's to the left, swap the columns 0 and 1"
About the "more efficient" part of the question...
Common setup for all tests (seed ensures sequences are identical):
import timeit
setup= '''
import numpy as np
np.random.seed(42)
my_array = np.random.random([10000,2])
swap = np.random.random([10000]) > 0.5
'''
All tests run for 1000 iterations
Original code: 5.621 seconds
timeit.timeit('swap_positions_conditionally(my_array, swap)', setup=setup, number=1000)
Added the definition of swap_positions_conditionally
to setup
as shown in the question.
This answer: 0.2657 seconds
timeit.timeit('my_array[swap, :] = my_array[swap,:][:,(1,0)]', setup=setup, number=1000)
Divakar's answer: 0.176 seconds
timeit.timeit('np.where(swap[:,None]!=1,my_array,my_array[:,::-1])', setup=setup, number=1000)
Yatu's first answer: 0.214 seconds
timeit.timeit('np.take_along_axis(my_array, np.c_[swap, 1-swap], axis=1)', setup=setup, number=1000)
Yatu's second answer: 0.2547 seconds
timeit.timeit('my_array[swap,:] = my_array[swap,::-1]', setup=setup, number=1000)
Conclusions
Profiling shows Divakar's version is the fastest. Whichever is more intuitive or readable is a matter of flavors, you can pick the one you prefer (I personally am a fan of the indexing notation readability-wise though...)
Advanced slicing: Python lists vs Numpy arrays
Why does this method of swapping not work for numpy arrays?
In short: Numpy arrays are true multidimensional objects and provide views on data. Python list
objects are not.
You are only ever indexing one list with l[...]
, and never the contents of the nested and independent row list
objects. Slicing also produces new list objects with references to the nested lists copied to the slice result.
Numpy slicing on the other hand addresses individual cells and series of cells in the whole matrix, and you are given views on the data; you can mutate the result of a numpy array slice and see the changes reflected in the original array.
The latter is what is going wrong for you here; as you assign the second row to the cells of the first row, the reference to the first row cells change with the assignment because they are not a copy but a view, and you end up copying the second row cells back to the second row via the first row.
So if you want to swap rows in a numpy array, you'll have to create a copy first:
source = arr.copy()
arr[::2], arr[1::2] = source[1::2], source[::2]
Selecting multiple slices from a numpy array at once
You can use the indexes to select the rows you want into the appropriate shape.
For example:
data = np.random.normal(size=(100,2,2,2))
# Creating an array of row-indexes
indexes = np.array([np.arange(0,5), np.arange(1,6), np.arange(2,7)])
# data[indexes] will return an element of shape (3,5,2,2,2). Converting
# to list happens along axis 0
data_extractions = list(data[indexes])
np.all(data_extractions[1] == data[1:6])
True
The final comparison is against the original data.
How can I swap two elements in one array?
#a=np.array([2,7])
a=[2,7]
# Reversing a list using slice notation
print (a[::-1]) # [7, 2]
# The reversed() method
print (list(reversed(a))) # [7, 2]
swap two elements in a list:
# Swap function
def swapPositions(list, pos1, pos2):
list[pos1], list[pos2] = list[pos2], list[pos1]
return list
a=[2,7]
pos1, pos2 = 0, 1
print(swapPositions(a, pos1 - 1, pos2 - 1))
What is mechanism of using sliced Numpy arrays in a tuple assignment (Python)?
So, this isn't simple assignment. Name-object binding semantics apply to simple assignment, i.e. a = b
.
If you do:
a[ix] = b
Then the exact behavior is deferred to the type, i.e., it is equivalent to
type(a).__setitem__(a, ix, b)
Numpy arrays are basically object-oriented wrappers over primitive, C-like arrays, implementing a "true" multidimensional array interface. In this context, the key thing to understand is that different numpy array objects can share underlying buffers. Simple slicing always creates a numpy.ndarray object that is a view over the original array.
So in this case, the b
above is actually a call to nd.array.__getitem__
. Which returns a view.
So, consider the simple case of Python lists. The right hand side:
(a[2:], a[:2])
Creates a tuple of two, independent list objects (although, shallow-copied).
When they are assigned to the sequence of assignment targets on the left-hand side, the mutation doesn't have any shared effect. There are three independent buffers for the three list objects (list objects will not create views).
On the other hand, the expression a[2:], a[:2]
creates a tuple with the result of slicing the original nd.array object, controlled by nd.array.__getitem__
. This creates two new array objects, but they are views over the underlying buffer from the original array. Basically, you are sharing the same underlying mutable, primitive buffer between three different array objects.
Related Topics
How to Change Effective Process Name in Python
Python Urllib2 with Keep Alive
Python: Changing Methods and Attributes at Runtime
How to Concatenate Two Layers in Keras
How to Stop Numpy from Multithreading
How to Tell Distutils to Use Gcc
Django Rest Framework Serializing Many to Many Field
How to Compare Two JSON Objects with the Same Elements in a Different Order Equal
How to Make Custom Legend in Matplotlib
How to Do Virtual File Processing
Python: One Try Multiple Except
Uploading Multiple Files with Flask
How to Create an Incrementing Filename in Python
Can Anyone Explain Python's Relative Imports
Using Subprocess to Run Python Script on Windows
How to Assign a Value to a Tensorflow Variable