简体   繁体   English

反转具有多个切片对象的 2D NumPy 数组

[英]Reverse a 2D NumPy array with multiple slice objects

Problem问题

I have a 2D NumPy array, arr , and for each row, I would like to reverse a section of the array.我有一个 2D NumPy 数组arr ,对于每一行,我想反转数组的一部分。 Crucially, for each row, the start and stop indices must be unique.至关重要的是,对于每一行, startstop索引必须是唯一的。 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.反转startstop索引之间的每一行。

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.我尝试使用reverse的输出来形成多个slice对象,但是没有成功。


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).对于我的数据,该解决方案只需要处理形状为 (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.在 Jupyter Notebook 中运行以下代码会得到以下结果。

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

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]]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM