在Numpy 2d数组中移动行的位置

在Numpy 2d数组中移动行的位置,numpy,Numpy,给定数组: np.array([[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]]) 如果要将标记1处的行移动到标记3。 输出应为: [[1, 2], [5, 6], [7, 8], [3, 4], [9, 10]] [[1, 2], [9, 10], [3, 4], [5, 6], [7, 8]] 如果我想将标记4处的行移动到标记1。 输出应为: [[1, 2],

给定数组:

np.array([[1, 2],
          [3, 4],
          [5, 6],
          [7, 8],
          [9, 10]])
如果要将标记1处的行移动到标记3。 输出应为:

[[1, 2],
 [5, 6],
 [7, 8],
 [3, 4],
 [9, 10]]
[[1, 2],
 [9, 10],
 [3, 4],
 [5, 6],
 [7, 8]]
如果我想将标记4处的行移动到标记1。 输出应为:

[[1, 2],
 [5, 6],
 [7, 8],
 [3, 4],
 [9, 10]]
[[1, 2],
 [9, 10],
 [3, 4],
 [5, 6],
 [7, 8]]
执行此移动操作的最快的方法是什么?

第一个轴上的
tuple()
索引如何

例如:

以及:

分别用于您的预期输出


要从两个索引开始生成索引,可以使用以下方法:

def inner_roll(arr, first, last, axis):
    stop = last + 1
    indices = list(range(arr.shape[axis]))
    indices.insert(first, last)
    indices.pop(last + 1)
    slicing = tuple(
        slice(None) if i != axis else indices
        for i, d in enumerate(arr.shape))
    return arr[slicing]
对于沿操作轴相对较小的输入(例如问题中的输入),这是相当快的

将其与稍微抛光的版本进行比较,以将其封装在函数中,并使其在任意
轴上正常工作

import numpy as np


def inner_roll2(arr, first, last, axis):
    if first > last:
        first, last = last, first
        shift = 1
    else:
        shift = -1
    slicing = tuple(
        slice(None) if i != axis else slice(first, last + 1)
        for i, d in enumerate(arr.shape))
    arr[slicing] = np.roll(arr[slicing], shift=shift, axis=axis)
    return arr
还有一些时间安排:

funcs = inner_roll, inner_roll2
for n in (5, 50, 500):
    for m in (2, 20, 200):
        arr = np.arange(n * m).reshape((n, m))
        print(f'({n:<3d}, {m:<3d})', end='    ')
        for func in funcs:
            results = %timeit -o -q func(arr, 1, 2, 0)
            print(f'{func.__name__:>12s}  {results.best* 1e6:>7.3f} µs', end='    ')
        print()
# (5  , 2  )      inner_roll    5.613 µs     inner_roll2   15.393 µs    
# (5  , 20 )      inner_roll    5.592 µs     inner_roll2   15.468 µs    
# (5  , 200)      inner_roll    5.916 µs     inner_roll2   15.815 µs    
# (50 , 2  )      inner_roll   10.117 µs     inner_roll2   15.517 µs    
# (50 , 20 )      inner_roll   10.360 µs     inner_roll2   15.505 µs    
# (50 , 200)      inner_roll   12.067 µs     inner_roll2   15.886 µs    
# (500, 2  )      inner_roll   55.833 µs     inner_roll2   15.409 µs    
# (500, 20 )      inner_roll   57.364 µs     inner_roll2   15.319 µs    
# (500, 200)      inner_roll  194.408 µs     inner_roll2   15.731 µs    
funcs=内部滚动,内部滚动2
对于n in(5,50,500):
对于m in(2,20,200):
arr=np.arange(n*m).重塑(n,m))
打印(f'({n:7.3f}µs',结束=“”)
打印()
#(5,2)内辊5.613µs内辊2 15.393µs
#(5,20)内辊5.592µs内辊2 15.468µs
#(5200)内辊5.916µs内辊2 15.815µs
#(50,2)内辊10.117µs内辊2 15.517µs
#(50,20)内辊10.360µs内辊2 15.505µs
#(50200)内辊12.067µs内辊2 15.886µs
#(500,2)内辊55.833µs内辊2 15.409µs
#(500,20)内辊57.364µs内辊2 15.319µs
#(500200)内辊194.408µs内辊2 15.731µs
这表明
internal_roll()
是最快的输入方法。 然而,
internal\u roll2()
似乎可以更好地扩展输入大小,即使是适度的输入大小,也比
internal\u roll()
更快

请注意,当
internal\u roll()
创建一个副本时,
internal\u roll2()
在适当的位置工作(修改输入
arr
)。可以通过在
internal\u roll2()
主体的开头添加
arr=arr.copy()
来修改此行为,这会使该功能变慢(当然)然后,它的计时将更受
m
(非滚动轴的大小)值的影响


另一方面,如果要执行多个连续的滚动操作,
internal\u roll 2()
计时就会叠加,而对于
internal\u roll())
你只需要做一次昂贵的部分。

如果你仔细看,如果你想把
i
行放在
j
位置,那么只有
i
j
之间的行受到影响;外部的行不需要更改。这种更改基本上是一种
滚动操作。对于
a项,b,c,d,e
,将项目置于
i=1
j=3
意味着
b,c,d
将变成
c,d,b
,给我们
a,c,d,b,e
。根据
我是否回答您的问题,移位是
-1
+1
?速度可能取决于必须移动(或上移/下移)的行数。从概念上讲,最简单的方法是使用高级索引创建一个新数组(如@norok2所示)。如果只切换更大数组中的几行,就地更改可能会更快(但您的示例移动了5中的3行和5中的4行)。是的,速度当然与受影响的行数成正比。高级索引访问所有数组元素,这就是为什么当行数增加时它的伸缩性很差。当然。虽然简单地插入正确排列的索引很容易,但您手动计算这些索引;这伸缩性很差。假设您有一个大小相同的数组(50005000),不可能像你写的那样手工写索引(0,2,3,1,4)。相反,您需要编写某种逻辑来创建正确排列的索引,这将消耗相当于长度为5000的数组的内存开销。我认为您的评论应该包含在norok的答案中。但您是对的,我的数组很大,因此我相信您的解决方案是最优的。@Mercury这并没有说明速度.查看对我答案的编辑。
i, j = 1,3
i, j, s = (i, j, -1) if i<j else (j, i, 1)
arr[i:j+1] = np.roll(arr[i:j+1],shift=s,axis=0)