从具有给定步幅/步长的 numpy 数组中获取子数组

Posted

技术标签:

【中文标题】从具有给定步幅/步长的 numpy 数组中获取子数组【英文标题】:Taking subarrays from numpy array with given stride/stepsize 【发布时间】:2017-02-26 08:25:56 【问题描述】:

假设我有一个 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 使用长度为 8 和步骤 4 的移动窗口查看 Y,但这会导致垃圾。你能纠正一下吗? 我之前用过as_strided,但发现它导致了非常严重的内存泄漏。这对于小型阵列来说不是问题,但即使在服务器上使用 64 GB 的 RAM,我的 python 程序也会引发 MemoryError。强烈推荐使用broadcasting_app 方法。 伙计,这太自动了!我正在实现 Shi-Tomasi 角点检测算法,我必须为每个像素创建一个窗口并计算一些复杂的东西。这个方法立刻给了我所有的窗口!!! @kkawabat 他们只是说我们在使用它时需要小心,了解它的作用。可以在更安全的方面添加 writeable 标志。 scikit-image 和 uses as_strided 等模块。 @AndyL。那么输入数组是一维的,所以n = a.strides[0]很好。【参考方案2】:

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

【讨论】:

【参考方案3】:

@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://***.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)

【讨论】:

以上是关于从具有给定步幅/步长的 numpy 数组中获取子数组的主要内容,如果未能解决你的问题,请参考以下文章

Numpy - 使用另一个数组的行从一个数组中删除行

当步长大于1时,通过数组切片和numpy.diff替换python中的for循环

numpy 切片

从给定的 2D numpy 数组中删除集群

33卷积步长讲解(Strided convolutions)

使用 NumPy 从矩阵中获取最小/最大 n 值和索引的有效方法