我有一份已分类的样本清单。他们按照他们的采样时间进行分类,其中每个样本在前一个样本之后一秒钟采样。 我想在指定大小的邻域中找到最小值。
例如,假设邻域大小为2且样本大小如下:
samples = [ 5, 12.3, 12.3, 7, 2, 6, 9, 10, 5, 9, 17, 2 ]
我期待以下输出:[5,2,5,2] 在numpy / scipy
中实现这一目标的最佳方法是什么?编辑:解释最小值背后的原因:
请注意,9分钟是否为左侧和右侧的2个窗口,值较小(2)
答案 0 :(得分:11)
使用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个大小邻域中的最小数字。由于5
仅支持2
或argrelextrema
边界条件,因此未检测到边界处的数字(clip
和wrap
)。至于你的问题,我猜你也对他们感兴趣。要检测它们,首先很容易添加反射边界条件:
>>> 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])
返回np.less
的滑动窗口中发生局部极值(本例中radius=2
以来的最小值)的位置。
请注意-radius
在+radius
边界条件为reflect
的数据包裹后修复np.pad
索引。
编辑:如果你对价值观感兴趣,而不是在立场,那就是直截了当:
>>> data[arg_minimas]
array([ 5., 2., 5., 2.])
答案 1 :(得分:2)
看起来,基本上你是在滑动窗口中找到局部最小值,但是滑动窗口以这样一种方式滑动,即前一个窗口的结尾作为新窗口的开始。对于这样一个特定问题,此解决方案中建议使用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:修改输入数据,窗口半径= 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:修改输入数据,窗口半径= 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 :(得分:1)
>>> 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在评论中指出的那样,这就是滑动窗口产生的结果。如果您想删除重复项,可以单独完成
答案 3 :(得分:1)
如果窗口的最小值不等于最近添加的值,则会查看每个窗口,找到最小值,并将其添加到列表中。
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的答案更好,因为如果它们在原始列表中有不同的索引,它将包含两个连续的相等最小值!