可以说我有一个Python Numpy数组a

a = numpy.array([1,2,3,4,5,6,7,8,9,10,11])


我想从这个长度为5的步长为3的数组创建一个子序列矩阵。因此,结果矩阵将如下所示:

numpy.array([[1,2,3,4,5],[4,5,6,7,8],[7,8,9,10,11]])


实现此目标的一种可能方法是使用for循环。

result_matrix = np.zeros((3, 5))
for i in range(0, len(a), 3):
  result_matrix[i] = a[i:i+5]


是否可以在Numpy中实现此目的的更简洁方法?

#1 楼

方法#1:使用broadcasting-

def broadcasting_app(a, L, S ):  # Window len = L, Stride len/stepsize = S
    nrows = ((a.size-L)//S)+1
    return a[S*np.arange(nrows)[:,None] + np.arange(L)]


方法2:使用更高效的NumPy strides-

def strided_app(a, L, S ):  # Window len = L, Stride len/stepsize = S
    nrows = ((a.size-L)//S)+1
    n = a.strides[0]
    return np.lib.stride_tricks.as_strided(a, shape=(nrows,L), strides=(S*n,n))


样品运行-

In [143]: a
Out[143]: array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

In [144]: broadcasting_app(a, L = 5, S = 3)
Out[144]: 
array([[ 1,  2,  3,  4,  5],
       [ 4,  5,  6,  7,  8],
       [ 7,  8,  9, 10, 11]])

In [145]: strided_app(a, L = 5, S = 3)
Out[145]: 
array([[ 1,  2,  3,  4,  5],
       [ 4,  5,  6,  7,  8],
       [ 7,  8,  9, 10, 11]])


评论


谢谢,我尝试了此操作:X = np.arange(100)Y = strided_app(X,4,1)给出了预期的Y,现在:Z = strided_app(Y,8,4)#我想让Z查看Y移动窗口的长度为8,步长为4,但这会导致垃圾。您能纠正吗?

–易失
17年1月19日在15:28



我以前使用过as_strided,但发现它导致了非常严重的内存泄漏。对于小型阵列而言,这不是问题,但是即使在服务器上使用64 GB的RAM,我的python程序也会引发MemoryError。强烈建议使用broadcast_app方法。

–pacificgilly1992
17年12月9日在8:27

杜德,这真是太神奇了!我正在实现Shi-Tomasi拐角检测算法,在该算法中,我必须为每个像素创建一个窗口并计算复杂的东西。这种方法立即给了我所有的窗户!

–insanely_sin
18-11-18在9:07



@kkawabat他们只是在说我们在使用它时需要小心,了解它的作用。该可写标志可以添加到更安全的一侧。像scikit-image这样的模块也使用as_strided。

– Divakar
19-10-30在18:09

@AndyL。好的输入数组是1D,所以n = a.strides [0]是好的。

– Divakar
9月18日下午5:35

#2 楼

@Divakar代码的修改版本,并进行检查以确保内存是连续的并且返回的数组无法修改。 (我的DSP应用程序的变量名称已更改。)

def frame(a, framelen, frameadv):
"""frame - Frame a 1D array
a - 1D array
framelen - Samples per frame
frameadv - Samples between starts of consecutive frames
   Set to framelen for non-overlaping consecutive frames

Modified from Divakar's 10/17/16 11:20 solution:
https://stackoverflow.com/questions/40084931/taking-subarrays-from-numpy-array-with-given-stride-stepsize

CAVEATS:
Assumes array is contiguous
Output is not writable as there are multiple views on the same memory

"""

if not isinstance(a, np.ndarray) or \
   not (a.flags['C_CONTIGUOUS'] or a.flags['F_CONTIGUOUS']):
    raise ValueError("Input array a must be a contiguous numpy array")

# Output
nrows = ((a.size-framelen)//frameadv)+1
oshape = (nrows, framelen)

# Size of each element in a
n = a.strides[0]

# Indexing in the new object will advance by frameadv * element size
ostrides = (frameadv*n, n)
return np.lib.stride_tricks.as_strided(a, shape=oshape,
                                       strides=ostrides, writeable=False)


#3 楼

Numpy 1.20开始,我们可以使用新的sliding_window_view来滑动/滚动元素窗口。
再加上步进[::3],它可以简单地变成:
from numpy.lib.stride_tricks import sliding_window_view

# values = np.array([1,2,3,4,5,6,7,8,9,10,11])
sliding_window_view(values, window_shape = 5)[::3]
# array([[ 1,  2,  3,  4,  5],
#        [ 4,  5,  6,  7,  8],
#        [ 7,  8,  9, 10, 11]])


滑动的中间结果是:
sliding_window_view(values, window_shape = 5)
# 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]])