Python:一个奇怪的Index out of Bounds案例

Fab*_*oli 2 python numpy

我写了一个代码来查找时间序列中的峰值,我希望它也可以绘制本地基线.目前我正在使用由两个cosinusoids构建的测试时间序列.

代码如下,其中p_times是峰值中心的时间:

step = 0.1  
time = np.arange(0, 10.1, step)

#Does stuff to find peaks

p_times = [0.9, 1., 1.1, 1.9, 2., 2.1, 2.9, 3., 3.1, 3.9, 4., 4.1, 4.9, 5., 5.1, 5.9, 6., 6.1, 6.9, 7., 7.1, 7.9, 8., 8.1, 8.9, 9., 9.1]

idx = np.array([np.where(time == x)[0][0] for x in p_times])
Run Code Online (Sandbox Code Playgroud)

最后一条指令应该给出一个数组,其中包含与峰值对应的时间元素的索引,但我得到:

IndexError: index 0 is out of bounds for axis 0 with size 0
Run Code Online (Sandbox Code Playgroud)

这个案例的好奇之处在于将cosinusoids参数更改为看似"幸运"的值,峰值的位置也会发生变化,代码可以正常工作:

p_times = [0.5, 1., 1.5, 2., 2.5, 3., 3.5, 4., 4.5, 5., 5.5, 6., 6.5, 7., 7.5, 8., 8.5, 9., 9.5]
# result: idx = [ 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95]
Run Code Online (Sandbox Code Playgroud)

更新:再次使用"不吉利"的时间序列,我有这个高峰时间阵列:

p_times =  [0.3, 1.8, 1.9, 2., 2.1, 2.2, 3.7, 3.8, 3.9, 4., 4.1, 4.2, 4.3, 5.8, 5.9, 6., 6.1, 6.2, 7.7, 7.8, 7.9, 8., 8.1, 8.2, 8.3]
Run Code Online (Sandbox Code Playgroud)

和指示:

idx_c = np.array([np.where(np.isclose(time, x))[0][0] for x in p_times])
Run Code Online (Sandbox Code Playgroud)

再次失败:

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-1-4c7f86bac90c> in <module>()
     53 #Baseline extremes (x,y), left and right
     54 #idx_c = np.array([np.where(time == x)[0][0] for x in O[:,0]])  #Cannot manage to vectorize this
---> 55 idx_c = np.array([np.where(np.isclose(time, x))[0][0] for x in p_times])
     56 print("idx_c = ", idx_c)
     57 idx_l = np.array(idx_c - k)  #Left extreme is at index of center (peak) minus k positions.

<ipython-input-1-4c7f86bac90c> in <listcomp>(.0)
     53 #Baseline extremes (x,y), left and right
     54 #idx_c = np.array([np.where(time == x)[0][0] for x in O[:,0]])  #Cannot manage to vectorize this
---> 55 idx_c = np.array([np.where(np.isclose(time, x))[0][0] for x in p_times])
     56 print("idx_c = ", idx_c)
     57 idx_l = np.array(idx_c - k)  #Left extreme is at index of center (peak) minus k positions.

IndexError: index 0 is out of bounds for axis 0 with size 0
Run Code Online (Sandbox Code Playgroud)

这种行为的原因是什么?

And*_*eak 5

您的方法的主要问题是您正在准确地比较浮点值.由于舍入错误,这几乎总是一个非常糟糕的主意,在这个臭名昭着的例子中证明:

>>> 0.1 + 0.2 == 0.3
False
Run Code Online (Sandbox Code Playgroud)

请注意,numpy double和本机python双打基本相似(在任何一种情况下,我都不确定大小依赖于体系结构,但你可能得到我的观点).

首先,您应该始终使用np.isclose/ np.allclose来比较浮点数是否相等.其次,这就是我发布完整答案的原因:你不必使用列表理解,你可以在一个numpy广播呼叫中做你想要的isclose:

>>> idx, data_idx = np.isclose(time[:,None], p_times).nonzero()
>>> idx
array([ 9, 10, 11, 19, 20, 21, 29, 30, 31, 39, 40, 41, 49, 50, 51, 59, 60,
       61, 69, 70, 71, 79, 80, 81, 89, 90, 91])
Run Code Online (Sandbox Code Playgroud)

这里发生的是time通过注入尾随单例维度将数组转换为2d列数组,并通过将每个time点与每个p_times点配对来构建bool矩阵.最后一次调用nonzero()返回True值的索引:第一个输出idx包含您正在寻找的索引.

此方法也更安全,因为如果峰值没有匹配时间,它将不会抛出异常.相反,你的idx价值比p_times积分少.在这种情况下,您将能够用于data_idx定位实际找到的峰的索引:

>>> data_idx
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16,
       17, 18, 19, 20, 21, 22, 23, 24, 25, 26])
Run Code Online (Sandbox Code Playgroud)