Swap Slices of Numpy Arrays

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 where swap is true"
  • my_array[swap,:] means "select the whole row where swap 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



Leave a reply



Submit