应用操作来不均匀地分割numpy数组的部分

时间:2017-02-24 20:15:17

标签: python numpy vectorization

我有三个1D numpy数组:

  1. 某些测量发生的时间列表(t)。
  2. ty)中每次发生的测量列表。
  3. 影响这些测量的一些外部变化的(较短的)时间列表(b)。
  4. 以下是一个例子:

    t = np.array([0.33856697,   1.69615293,   1.70257872,   2.32510279,
                  2.37788203,   2.45102176,   2.87518307,   3.60941650,
                  3.78275907,   4.37970516,   4.56480259,   5.33306546,
                  6.00867792,   7.40217571,   7.46716989,   7.6791613 ,
                  7.96938078,   8.41620336,   9.17116349,  10.87530965])
    y = np.array([ 3.70209916,  6.31148802,  2.96578172,  3.90036915, 5.11728629,
                   2.85788050,  4.50077811,  4.05113322,  3.55551093, 7.58624384,
                   5.47249362,  5.00286872,  6.26664832,  7.08640263, 5.28350628,
                   7.71646500,  3.75513591,  5.72849991,  5.60717179, 3.99436659])
    
    b = np.array([ 1.7,  3.9,  9.5])
    

    b的元素落在粗体和斜体元素t之间,将其分成四个长度不等的长度为2,7,10,1的片段。

    我想对y的每个段应用一个操作来获取大小为b.size + 1的数组。具体来说,我想知道更多是否超过每个段中y的一半值超出或低于某个偏差。

    我目前正在使用for循环和切片来应用我的测试:

    bias = 5
    categories = np.digitize(t, b)
    result = np.empty(b.size + 1, dtype=np.bool_)
    for i in range(result.size):
        mask = (categories == i)
        result[i] = (np.count_nonzero(y[mask] > bias) / np.count_nonzero(mask)) > 0.5
    

    这似乎效率极低。不幸的是,np.where在这种情况下没有多大帮助。有没有办法对我在这里描述的操作进行矢量化以避免Python for循环?

    顺便说一下,这是ytbias以及由b分隔的区域的图,以说明预期result的原因是array([False, False, True, False], dtype=bool)

    enter image description here

    生成
    from matplotlib import pyplot as plt
    from matplotlib.patches import Rectangle
    plt.ion()
    f, a = plt.subplots()
    a.plot(t, y, label='y vs t')
    a.hlines(5, *a.get_xlim(), label='bias')
    plt.tight_layout()
    a.set_xlim(0, 11)
    c = np.concatenate([[0], b, [11]])
    for i in range(len(c) - 1):
        a.add_patch(Rectangle((c[i], 2.5), c[i+1] - c[i], 8 - 2.5, alpha=0.2, color=('red' if i % 2 else 'green'), zorder=-i-5))
    a.legend()
    

1 个答案:

答案 0 :(得分:2)

不应该产生相同的结果吗?

split_points = np.searchsorted(t, np.r_[t[0], b, t[-1]])
numerator = np.add.reduceat(y > bias, split_points[:-1])
denominator = np.diff(split_points)
result = (numerator / denominator) > 0.5

很少有人注意到:这种方法依赖于排序。那么相对于b的bin将都是整齐的块,所以我们不需要掩码来描述它们,而只需要将索引形式的端点描述为t。这是searchsorted为我们找到的内容。

由于您的标准似乎不依赖于群组,因此我们可以一次性为所有y制作一个大面具。在布尔数组中计算非零值与求和相同,因为True将被强制转换为等等。在这种情况下的优点是我们可以使用add.reduceat来获取数组,一个拆分列表点然后对分裂之间的块进行求和,这正是我们想要的。

为了规范化,我们需要计算每个bin中的总数,但由于这些bin是连续的,我们只需要使用split_points来区分该bin,这就是我们使用diff的位置。