numpy 如何在一维数组的邻域中找到局部最小值
numpy how find local minimum in neighborhood on 1darray
我有一个已排序样本的列表。它们按采样时间排序,每个采样都比前一个采样晚一秒。
我想在指定大小的邻域中找到最小值。
例如,给定邻域大小 2 和以下样本大小:
samples = [ 5, 12.3, 12.3, 7, 2, 6, 9, 10, 5, 9, 17, 2 ]
我希望得到以下输出:[5, 2, 5, 2]
在 numpy / scipy
中实现此目标的最佳方法是什么
已编辑:解释最小值背后的原因:
- 5 - 旁边的 2 个数字 window 是 [12.3 12.3]。 5更小
- 2 - 向左 [12.3, 7] 向右 [6 9]。 2 是最小值
- 5 - 向左 [9 10] 向右 [9 17]。 5 是最小值
注意 9 不是最小值,它的左右各有一个 2 window,其值较小 (2)
>>> import numpy as np
>>> a = np.array(samples)
>>> [a[max(i-2,0):i+2].min() for i in xrange(1, a.size)]
[5.0, 2.0, 2.0, 2.0, 2.0, 5.0, 5.0, 5.0, 2.0]
正如 Divakar 在评论中指出的那样,这就是滑动 window 的结果。如果您想删除重复项,可以单独完成
这将遍历每个 window,找到最小值,如果 window 的最小值不等于最近添加的值,则将其添加到列表中。
samples = [5, 12.3, 12.3, 7, 2, 6, 9, 10, 5, 9, 17, 2]
neighborhood = 2
minima = []
for i in xrange(len(samples)):
window = samples[max(0, i - neighborhood):i + neighborhood + 1]
windowMin = min(window)
if minima == [] or windowMin != minima[-1]:
minima.append(windowMin)
这给出了您描述的输出:
print minima
> [5, 2, 5, 2]
但是,@imaluengo 的答案更好,因为如果它们在原始列表中具有不同的索引,它将包括两个连续的相等最小值!
使用scipy的argrelextrema
:
>>> import numpy as np
>>> from scipy.signal import argrelextrema
>>> data = np.array([ 5, 12.3, 12.3, 7, 2, 6, 9, 10, 5, 9, 17, 2 ])
>>> radius = 2 # number of elements to the left and right to compare to
>>> argrelextrema(data, np.less, order=radius)
(array([4, 8]),)
这表明位置 4 和 8(2
和 5
)的数字是 2 大小邻域内最小的数字。由于argrelextrema
仅支持clip
或wrap
边界条件,因此未检测到边界(5
和2
)处的数字。至于你的问题,我想你也对它们感兴趣。要检测它们,很容易先添加反射边界条件:
>>> new_data = np.pad(data, radius, mode='reflect')
>>> new_data
array([ 12.3, 12.3, 5. , 12.3, 12.3, 7. , 2. , 6. , 9. ,
10. , 5. , 9. , 17. , 2. , 17. , 9. ])
有了相应边界条件的数据,我们现在可以应用previus极值检测器:
>>> arg_minimas = argrelextrema(new_data, np.less, order=radius)[0] - radius
>>> arg_minimas
array([ 0, 4, 8, 11])
returns 局部极值(在本例中自 np.less
以来的最小值)发生在 radius=2
的滑动 window 中的位置。
注意 -radius
在用 reflect
边界条件和 np.pad
包装数组后修复 +radius
索引。
编辑:如果您对值而不是位置感兴趣,那就很简单了:
>>> data[arg_minimas]
array([ 5., 2., 5., 2.])
看起来,基本上您是在滑动 window 中找到局部最小值,但是滑动 window 的滑动方式使得前一个 window 的结尾充当开始新的 window。对于此类特定问题,此解决方案中建议使用 broadcasting
-
的矢量化方法
import numpy as np
# Inputs
N = 2
samples = [ 5, 12.3, 12.3, 7, 2, 6, 9, 10, 5, 9, 17, 2 ]
# Convert input list to a numpy array
S = np.asarray(samples)
# Calculate the number of Infs to be appended at the end
append_endlen = int(2*N*np.ceil((S.size+1)/(2*N))-1 - S.size)
# Append Infs at the start and end of the input array
S1 = np.concatenate((np.repeat(np.Inf,N),S,np.repeat(np.Inf,append_endlen)),0)
# Number of sliding windows
num_windows = int((S1.size-1)/(2*N))
# Get windowed values from input array into rows.
# Thus, get minimum from each row to get the desired local minimum.
indexed_vals = S1[np.arange(num_windows)[:,None]*2*N + np.arange(2*N+1)]
out = indexed_vals.min(1)
样本运行
运行 # 1: 原始输入数据
In [105]: S # Input array
Out[105]:
array([ 5. , 12.3, 12.3, 7. , 2. , 6. , 9. , 10. , 5. ,
9. , 17. , 2. ])
In [106]: N # Window radius
Out[106]: 2
In [107]: out # Output array
Out[107]: array([ 5., 2., 5., 2.])
运行 # 2:修改输入数据,Window radius = 2
In [101]: S # Input array
Out[101]:
array([ 5. , 12.3, 12.3, 7. , 2. , 6. , 9. , 10. , 5. ,
9. , 17. , 2. , 0. , -3. , 7. , 99. , 1. , 0. ,
-4. , -2. ])
In [102]: N # Window radius
Out[102]: 2
In [103]: out # Output array
Out[103]: array([ 5., 2., 5., -3., -4., -4.])
运行 # 3:修改输入数据,Window radius = 3
In [97]: S # Input array
Out[97]:
array([ 5. , 12.3, 12.3, 7. , 2. , 6. , 9. , 10. , 5. ,
9. , 17. , 2. , 0. , -3. , 7. , 99. , 1. , 0. ,
-4. , -2. ])
In [98]: N # Window radius
Out[98]: 3
In [99]: out # Output array
Out[99]: array([ 5., 2., -3., -4.])
我有一个已排序样本的列表。它们按采样时间排序,每个采样都比前一个采样晚一秒。 我想在指定大小的邻域中找到最小值。
例如,给定邻域大小 2 和以下样本大小:
samples = [ 5, 12.3, 12.3, 7, 2, 6, 9, 10, 5, 9, 17, 2 ]
我希望得到以下输出:[5, 2, 5, 2] 在 numpy / scipy
中实现此目标的最佳方法是什么已编辑:解释最小值背后的原因:
- 5 - 旁边的 2 个数字 window 是 [12.3 12.3]。 5更小
- 2 - 向左 [12.3, 7] 向右 [6 9]。 2 是最小值
- 5 - 向左 [9 10] 向右 [9 17]。 5 是最小值
注意 9 不是最小值,它的左右各有一个 2 window,其值较小 (2)
>>> import numpy as np
>>> a = np.array(samples)
>>> [a[max(i-2,0):i+2].min() for i in xrange(1, a.size)]
[5.0, 2.0, 2.0, 2.0, 2.0, 5.0, 5.0, 5.0, 2.0]
正如 Divakar 在评论中指出的那样,这就是滑动 window 的结果。如果您想删除重复项,可以单独完成
这将遍历每个 window,找到最小值,如果 window 的最小值不等于最近添加的值,则将其添加到列表中。
samples = [5, 12.3, 12.3, 7, 2, 6, 9, 10, 5, 9, 17, 2]
neighborhood = 2
minima = []
for i in xrange(len(samples)):
window = samples[max(0, i - neighborhood):i + neighborhood + 1]
windowMin = min(window)
if minima == [] or windowMin != minima[-1]:
minima.append(windowMin)
这给出了您描述的输出:
print minima
> [5, 2, 5, 2]
但是,@imaluengo 的答案更好,因为如果它们在原始列表中具有不同的索引,它将包括两个连续的相等最小值!
使用scipy的argrelextrema
:
>>> import numpy as np
>>> from scipy.signal import argrelextrema
>>> data = np.array([ 5, 12.3, 12.3, 7, 2, 6, 9, 10, 5, 9, 17, 2 ])
>>> radius = 2 # number of elements to the left and right to compare to
>>> argrelextrema(data, np.less, order=radius)
(array([4, 8]),)
这表明位置 4 和 8(2
和 5
)的数字是 2 大小邻域内最小的数字。由于argrelextrema
仅支持clip
或wrap
边界条件,因此未检测到边界(5
和2
)处的数字。至于你的问题,我想你也对它们感兴趣。要检测它们,很容易先添加反射边界条件:
>>> new_data = np.pad(data, radius, mode='reflect')
>>> new_data
array([ 12.3, 12.3, 5. , 12.3, 12.3, 7. , 2. , 6. , 9. ,
10. , 5. , 9. , 17. , 2. , 17. , 9. ])
有了相应边界条件的数据,我们现在可以应用previus极值检测器:
>>> arg_minimas = argrelextrema(new_data, np.less, order=radius)[0] - radius
>>> arg_minimas
array([ 0, 4, 8, 11])
returns 局部极值(在本例中自 np.less
以来的最小值)发生在 radius=2
的滑动 window 中的位置。
注意 -radius
在用 reflect
边界条件和 np.pad
包装数组后修复 +radius
索引。
编辑:如果您对值而不是位置感兴趣,那就很简单了:
>>> data[arg_minimas]
array([ 5., 2., 5., 2.])
看起来,基本上您是在滑动 window 中找到局部最小值,但是滑动 window 的滑动方式使得前一个 window 的结尾充当开始新的 window。对于此类特定问题,此解决方案中建议使用 broadcasting
-
import numpy as np
# Inputs
N = 2
samples = [ 5, 12.3, 12.3, 7, 2, 6, 9, 10, 5, 9, 17, 2 ]
# Convert input list to a numpy array
S = np.asarray(samples)
# Calculate the number of Infs to be appended at the end
append_endlen = int(2*N*np.ceil((S.size+1)/(2*N))-1 - S.size)
# Append Infs at the start and end of the input array
S1 = np.concatenate((np.repeat(np.Inf,N),S,np.repeat(np.Inf,append_endlen)),0)
# Number of sliding windows
num_windows = int((S1.size-1)/(2*N))
# Get windowed values from input array into rows.
# Thus, get minimum from each row to get the desired local minimum.
indexed_vals = S1[np.arange(num_windows)[:,None]*2*N + np.arange(2*N+1)]
out = indexed_vals.min(1)
样本运行
运行 # 1: 原始输入数据
In [105]: S # Input array
Out[105]:
array([ 5. , 12.3, 12.3, 7. , 2. , 6. , 9. , 10. , 5. ,
9. , 17. , 2. ])
In [106]: N # Window radius
Out[106]: 2
In [107]: out # Output array
Out[107]: array([ 5., 2., 5., 2.])
运行 # 2:修改输入数据,Window radius = 2
In [101]: S # Input array
Out[101]:
array([ 5. , 12.3, 12.3, 7. , 2. , 6. , 9. , 10. , 5. ,
9. , 17. , 2. , 0. , -3. , 7. , 99. , 1. , 0. ,
-4. , -2. ])
In [102]: N # Window radius
Out[102]: 2
In [103]: out # Output array
Out[103]: array([ 5., 2., 5., -3., -4., -4.])
运行 # 3:修改输入数据,Window radius = 3
In [97]: S # Input array
Out[97]:
array([ 5. , 12.3, 12.3, 7. , 2. , 6. , 9. , 10. , 5. ,
9. , 17. , 2. , 0. , -3. , 7. , 99. , 1. , 0. ,
-4. , -2. ])
In [98]: N # Window radius
Out[98]: 3
In [99]: out # Output array
Out[99]: array([ 5., 2., -3., -4.])