Reverse a 2D NumPy array with multiple slice objects

有些话、适合烂在心里 提交于 2021-02-08 08:19:57

问题


Problem

I have a 2D NumPy array, arr, and for each row, I would like to reverse a section of the array. Crucially, for each row, the start and stop indices must be unique. I can achieve this using the following.

import numpy as np

arr = np.repeat(np.arange(10)[np.newaxis, :], 3, axis=0)
reverse = np.sort(np.random.choice(arr.shape[1], [arr.shape[0], 2], False))

# arr
# array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
#        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
#        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]])

# reverse
# array([[1, 7],
#        [8, 9],
#        [4, 6]])

Reverse each row between the start, stop indices in `reverse.

for idx, (i, j) in enumerate(reverse):
    arr[idx, i:j+1] = arr[idx, i:j+1][::-1]

# arr 
# array([[0, 7, 6, 5, 4, 3, 2, 1, 8, 9],
#        [0, 1, 2, 3, 4, 5, 6, 7, 9, 8],
#        [0, 1, 2, 3, 6, 5, 4, 7, 8, 9]])

Question

Is this possible using basic slicing and indexing? I tried to use the output of reverse to form multiple slice objects, but was unsuccessful.


Update

A simple comparison of the original method vs answer. For my data, the solution is only required to deal with 2D matrices with shape (50, 100).

import numpy as np

def reverse_one(arr, n): 
    temp = np.repeat(arr.copy(), n, axis=0)
    reverse = np.sort(np.random.choice(temp.shape[1], [n, 2], False))

    for idx, (i, j) in enumerate(reverse):
        temp[idx, i:j+1] = temp[idx, i:j+1][::-1]

    return temp

def reverse_two(arr, n):
    temp = np.repeat(arr.copy(), n, axis=0)
    reverse = np.sort(np.random.choice(temp.shape[1], [n, 2], False))
    rev = np.ravel_multi_index((np.arange(n)[:, np.newaxis], reverse), temp.shape)
    rev[:, 1] += 1
    idx = np.arange(temp.size).reshape(temp.shape)
    s = np.searchsorted(rev.ravel(), idx, 'right')
    m = (s % 2 == 1)
    g = rev[s[m] // 2]
    idx[m] = g[:, 0] - (idx[m] - g[:, 1]) - 1

    return temp.take(idx)

m = 100
arr = np.arange(m)[np.newaxis, :]

print("reverse_one:")
%timeit reverse_one(arr, m//2)
print("=" * 40)
print("reverse_two:")
%timeit reverse_two(arr, m//2)

Running the following code in a Jupyter Notebook gives the following results.

reverse_one:
1000 loops, best of 5: 202 µs per loop
========================================
reverse_two:
1000 loops, best of 5: 363 µs per loop

回答1:


This was kinda tricky but I figured out one way to do it. Advanced indexing is expensive though so you'd have to see whether it is really faster or not depending on the data that you have.

import numpy as np

np.random.seed(0)
arr = np.repeat(np.arange(10)[np.newaxis, :], 3, axis=0)
reverse = np.sort(np.random.choice(arr.shape[1], [arr.shape[0], 2], False))
print(arr)
# [[0 1 2 3 4 5 6 7 8 9]
#  [0 1 2 3 4 5 6 7 8 9]
#  [0 1 2 3 4 5 6 7 8 9]]
print(reverse)
# [[2 8]
#  [4 9]
#  [1 6]]

# Get "flat" indices of the bounds
rev = np.ravel_multi_index((np.arange(arr.shape[0])[:, np.newaxis], reverse), arr.shape)
# Add one to the second bound (so it is first index after the slice)
rev[:, 1] += 1
# Make array of flat indices for the data
idx = np.arange(arr.size).reshape(arr.shape)
# Find the position of flat indices with respect to bounds
s = np.searchsorted(rev.ravel(), idx, 'right')
# For each "i" within a slice, "s[i]" is odd
m = (s % 2 == 1)
# Replace indices within slices with their reversed ones
g = rev[s[m] // 2]
idx[m] = g[:, 0] - (idx[m] - g[:, 1]) - 1
# Apply indices to array
res = arr.take(idx)
print(res)
# [[0 1 8 7 6 5 4 3 2 9]
#  [0 1 2 3 9 8 7 6 5 4]
#  [0 6 5 4 3 2 1 7 8 9]]


来源:https://stackoverflow.com/questions/59951813/reverse-a-2d-numpy-array-with-multiple-slice-objects

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!