连续的,重叠的数组子集(NumPy,Python)

Sne*_*ney 12 python numpy scipy

我有一个NumPy数组,[1,2,3,4,5,6,7,8,9,10,11,12,13,14]并希望有一个像这样的数组[[1,2,3,4], [2,3,4,5], [3,4,5,6], ..., [11,12,13,14]].

当然这可以通过循环大数组并将长度为4的数组添加到新数组中,但我很好奇是否有一些秘密的'魔术'Python方法正在做这个:)

Pau*_*aul 26

你应该用stride_tricks.当我第一次看到这个时,"魔法"这个词就浮现在脑海中.它很简单,是迄今为止最快的方法.

>>> as_strided = numpy.lib.stride_tricks.as_strided
>>> a = numpy.arange(1,15)
>>> a
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])
>>> b = as_strided(a, (11,4), a.strides*2)
>>> b
array([[ 1,  2,  3,  4],
       [ 2,  3,  4,  5],
       [ 3,  4,  5,  6],
       [ 4,  5,  6,  7],
       [ 5,  6,  7,  8],
       [ 6,  7,  8,  9],
       [ 7,  8,  9, 10],
       [ 8,  9, 10, 11],
       [ 9, 10, 11, 12],
       [10, 11, 12, 13],
       [11, 12, 13, 14]])
Run Code Online (Sandbox Code Playgroud)

请注意,数组b中的值是以a不同的方式查看的.做一个.copy()b,如果你打算修改它.

我在SciPy会议上看到了这一点.以下是幻灯片以获得更多解释.

  • 首先,我想说这是一个很棒的解决方案,而不是指出来!请注意,在我的机器上,步长为8位长度(可以使用a.strides进行检查,如您提供的链接中所示) - 可能是64位与32位架构默认值(虽然我没有验证).也就是说,我机器上的命令是:>>> b = as_strided(a,(11,4),(8,8)) (3认同)
  • @ eldad-a为具体起见,请使用b = as_strided(a,(11,4),(a.strides [0],a.strides [0]))` (2认同)

Joh*_*ooy 16

最快的方法似乎是预分配数组,在本答案的底部作为选项7给出.

>>> import numpy as np
>>> A=np.array([1,2,3,4,5,6,7,8,9,10,11,12,13,14])
>>> A
array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])
>>> np.array(zip(A,A[1:],A[2:],A[3:]))
array([[ 1,  2,  3,  4],
       [ 2,  3,  4,  5],
       [ 3,  4,  5,  6],
       [ 4,  5,  6,  7],
       [ 5,  6,  7,  8],
       [ 6,  7,  8,  9],
       [ 7,  8,  9, 10],
       [ 8,  9, 10, 11],
       [ 9, 10, 11, 12],
       [10, 11, 12, 13],
       [11, 12, 13, 14]])
>>> 
Run Code Online (Sandbox Code Playgroud)

您可以轻松地对此进行调整以实现可变块大小.

>>> n=5
>>> np.array(zip(*(A[i:] for i in range(n))))
array([[ 1,  2,  3,  4,  5],
       [ 2,  3,  4,  5,  6],
       [ 3,  4,  5,  6,  7],
       [ 4,  5,  6,  7,  8],
       [ 5,  6,  7,  8,  9],
       [ 6,  7,  8,  9, 10],
       [ 7,  8,  9, 10, 11],
       [ 8,  9, 10, 11, 12],
       [ 9, 10, 11, 12, 13],
       [10, 11, 12, 13, 14]])
Run Code Online (Sandbox Code Playgroud)

您可能希望比较此和使用之间的性能itertools.islice.

>>> from itertools import islice
>>> n=4
>>> np.array(zip(*[islice(A,i,None) for i in range(n)]))
array([[ 1,  2,  3,  4],
       [ 2,  3,  4,  5],
       [ 3,  4,  5,  6],
       [ 4,  5,  6,  7],
       [ 5,  6,  7,  8],
       [ 6,  7,  8,  9],
       [ 7,  8,  9, 10],
       [ 8,  9, 10, 11],
       [ 9, 10, 11, 12],
       [10, 11, 12, 13],
       [11, 12, 13, 14]])
Run Code Online (Sandbox Code Playgroud)

我的时间结果:

1. timeit np.array(zip(A,A[1:],A[2:],A[3:]))
10000 loops, best of 3: 92.9 us per loop

2. timeit np.array(zip(*(A[i:] for i in range(4))))
10000 loops, best of 3: 101 us per loop

3. timeit np.array(zip(*[islice(A,i,None) for i in range(4)]))
10000 loops, best of 3: 101 us per loop

4. timeit numpy.array([ A[i:i+4] for i in range(len(A)-3) ])
10000 loops, best of 3: 37.8 us per loop

5. timeit numpy.array(list(chunks(A, 4)))
10000 loops, best of 3: 43.2 us per loop

6. timeit numpy.array(byN(A, 4))
10000 loops, best of 3: 100 us per loop

# Does preallocation of the array help? (11 is from len(A)+1-4)
7. timeit B=np.zeros(shape=(11, 4),dtype=np.int32)
100000 loops, best of 3: 2.19 us per loop
   timeit for i in range(4):B[:,i]=A[i:11+i]
10000 loops, best of 3: 20.9 us per loop
total 23.1us per loop
Run Code Online (Sandbox Code Playgroud)

随着len(A)增加(20000)4和5收敛到等效速度(44ms).1,2,3和6都保持慢约3倍(135毫秒).7更快(1.36毫秒).

  • 如果我真的对这个进行基准测试(一个愚蠢而又有趣的任务),我可能会尝试通过使用`numpy.empty`而不是`numpy.zeros`并使用列主要数组来减少几纳秒.我在设置列时处理连续的内存. (2认同)