Python:索引超出界限的奇怪案例

时间:2018-07-25 11:48:22

标签: python numpy

我写了一段代码来查找时间序列中的峰,我也希望它能绘制局部基线。目前,我正在使用由两个余弦曲线构建的测试时间序列。

代码如下,其中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])

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

IndexError: index 0 is out of bounds for axis 0 with size 0

使情况变得奇怪的是,将余弦曲线参数更改为看起来有些“幸运”的值,峰的位置也发生了变化,并且代码有效:

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]

更新:再次使用“不幸的”时间序列,我得到了以下高峰时间数组:

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]

说明:

idx_c = np.array([np.where(np.isclose(time, x))[0][0] for x in p_times])

再次失败:

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

此行为的原因是什么?

1 个答案:

答案 0 :(得分:5)

您的方法的主要问题是要精确比较浮点值。由于四舍五入的错误,这几乎总是一个很糟糕的主意,在这个臭名昭著的例子中得到了证明:

>>> 0.1 + 0.2 == 0.3
False

请注意,numpy双打和本机python双打在本质上是相似的(无论哪种情况,我都不确定大小取决于体系结构的依赖性,但是您可能会明白我的意思)。

因此,首先,您应始终使用np.isclose / np.allclose来比较浮点数是否相等。其次,这就是我要发布完整答案的原因:您不必使用列表理解,您可以在对isclose的单个numpy广播呼叫中执行所需的操作:

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

这里发生的是,通过插入尾随单例维,将time数组转换为2d列数组,并且通过将每个time点与每个{{1 }}点。对p_times的最终调用返回nonzero()值的索引:第一个输出True包含您要查找的索引。

此方法也更安全,因为如果峰没有匹配时间,它将不会引发异常。相反,您拥有的idx值要少于idx点。在这种情况下,您可以使用p_times来找到实际发现的峰的索引:

data_idx