通过向前移动其他元素来替换元素中元素的位置 - NumPy

时间:2016-10-30 19:30:44

标签: arrays numpy

搜索之后,我找不到原生方式或当前解决方案来有效地改变numpy数组中元素的位置,这在我看来非常自然。例如,如果我想在第一个位置移动第3个元素,它应该是这样的:

x = np.array([1,2,3,4,5])
f*(x, 3, 1)

print x
array([1,4,2,3,5])

我在这里寻找一个f *功能。这不同于滚动每个元素,也适用于大数组中的移动我想避免使用插入和删除操作可以使用的复制操作

3 个答案:

答案 0 :(得分:2)

不确定效率,但这是使用masking -

的方法
def change_pos(in_arr, pick_idx, put_idx ):    
    range_arr = np.arange(in_arr.size)  
    tmp = in_arr[pick_idx]
    in_arr[range_arr != put_idx ] = in_arr[range_arr != pick_idx]
    in_arr[put_idx] = tmp

这将支持向前和向后移动。

示例运行

1)元素向后移动 -

In [542]: in_arr
Out[542]: array([4, 9, 3, 6, 8, 0, 2, 1])
                                   *    
In [543]: change_pos(in_arr,6,1)

In [544]: in_arr
Out[544]: array([4, 2, 9, 3, 6, 8, 0, 1])
                    ^

2)元素向前发展 -

In [546]: in_arr
Out[546]: array([4, 9, 3, 6, 8, 0, 2, 1])
                    *
In [547]: change_pos(in_arr,1,6)

In [548]: in_arr
Out[548]: array([4, 3, 6, 8, 0, 2, 9, 1])
                                   ^

答案 1 :(得分:2)

通过一个小例子,这个批发拷贝的测试速度比@Divakar掩盖的就地拷贝更快:

def foo4(arr, i,j):
    L=arr.shape[0]
    idx=np.concatenate((np.arange(j),[i],np.arange(j,i),np.arange(i+1,L)))
    return arr[idx]

我没有尝试让它适用于前进动作。类似的就地功能与Divakar的速度大致相同。

def foo2(arr, i,j):
    L=arr.shape[0]
    tgt=np.arange(j,i+1)
    src=np.concatenate([[i],np.arange(j,i)])
    arr[tgt]=arr[src]

但是如果阵列更大并且交换涉及中间的一个小块,那么时间可能会有所不同。

由于数组的数据存储在连续的内存块中,因此如果没有某种副本,元素就无法更改。您可以将实现列表作为链接列表,以便具有无副本移动形式。

我刚刚想到有一些蒙版的copytoplace函数可能会使这种复制/移动速度更快。但是我没有那么多工作。

https://stackoverflow.com/a/40228699/901925

=====

np.roll

idx = np.concatenate((np.arange(2,5),np.arange(2)))
#  array([2, 3, 4, 0, 1])
np.take(a, idx)   # or a[idx]

答案 2 :(得分:1)

在过去,我发现简单的numpy索引,即a[:-1]=a[1:]比大多数替代(包括np.roll())更快。将其他两个答案与“#”进行比较'我得到了转变:

从40000变为100

1.015ms divakar
1.078ms hpaulj
29.7micro s in place shift (34 x faster)

从40000转换到39900

0.975ms divakar
0.985ms hpaulj
3.47micro s in place shift (290 x faster)

时序比较使用:

import timeit

init = '''
import numpy as np

def divakar(in_arr, pick_idx, put_idx ):    
    range_arr = np.arange(in_arr.size)  
    tmp = in_arr[pick_idx]
    in_arr[range_arr != put_idx ] = in_arr[range_arr != pick_idx]
    in_arr[put_idx] = tmp

def hpaulj(arr, fr, to):
  L = arr.shape[0]
  idx = np.concatenate((np.arange(to), [fr], np.arange(to, fr), np.arange(fr+1, L)))
  return arr[idx]

def paddyg(arr, fr, to):
  if fr >= arr.size or to >= arr.size:
    return None
  tmp = arr[fr].copy()
  if fr > to:
    arr[to+1:fr+1] = arr[to:fr]
  else:
    arr[fr:to] = arr[fr+1:to+1]
  arr[to] = tmp
  return arr

a = np.random.randint(0, 1000, (100000))
'''


fns = ['''
divakar(a, 40000, 100)
''', '''
hpaulj(a, 40000, 100)
''', '''
paddyg(a, 40000, 100)
''']

for f in fns:
  print(timeit.timeit(f, setup=init, number=1000))