Numpy:获取数组中边界的索引,其中边界的起点总是以特定的数字开头;通过一个特定的数字无边界



问题

获得阵列中边界索引的计算效率最高的解决方案,其中边界的开始总是以特定的数字开始,而非边界由不同的特定数字指示。

此问题与SO上其他基于边界的numpy问题之间的差异:

以下是其他一些基于边界的numpy问题

Numpy 1D数组-查找相同编号的子序列的边界索引

用孔获取numpy阵列形状的边界

数字阵列的边界提取

在我试图寻找解决方案的过程中,我所问的问题与其他stackoverflow帖子的不同之处在于,其他边界是由值的跳跃或值的"洞"表示的。

在我的案例中,似乎唯一的是边界的开始总是以一个特定的数字开始。

动机:

这个问题的灵感来源于自然语言处理中的IOB标记。在IOB标记中,单词的开头标记有B[开头]是实体中第一个字母的标记,I[内部]是单词中除第一个字符外的所有其他字符的标记,[O]用于标记所有非实体字符

示例:

import numpy as np
a = np.array(
[
0, 0, 0, 1, 2, 2, 2, 0, 0, 0, 1, 0, 0, 1, 2, 1, 1, 0, 0, 1, 1, 1
]
)

1是每个边界的起点。如果边界的长度大于1,则2构成边界的其余部分。0是非边界数字。

这些边界的实体是1, 2, 2, 211,211111

因此,所需的解决方案;CCD_ 9的索引边界值的索引是

desired = [[3, 6], [10, 10], [13, 14], [15, 15], [16,16], [19,19], [20,20], [21,21]]

当前解决方案:

如果展开,则所需解决方案中的数字按升序排列。因此,可以稍后对原始索引号进行计算、排序和整形。

我可以使用获得起始索引

starts = np.where(a==1)[0]
starts
array([ 3, 10, 13, 15, 16, 19, 20, 21])

所以剩下的是6, 10, 14, 15,16,19,20,21

我可以使用3个不同的条件来获得除1之外的所有值,其中我可以通过减少值和非移位数组的值来将移位数组与原始数组进行比较。

first = np.where(a[:-1] - 2 == a[1:])[0]
first 
array([6])
second = np.where((a[:-1] - 1 == a[1:]) & 
((a[1:]==1) | (a[1:]==0)))[0]
second
array([10, 14, 16])
third = np.where(
(a[:-1] == a[1:]) &
(a[1:]==1)
)[0]
third
array([15, 19, 20])

我需要的最后一个数字是21,但由于我需要将数组的长度缩短1来进行移位比较,所以我不确定如何使用逻辑来获得该特定值,所以我只使用了一个简单的if语句。

使用索引的其余检索值,我可以将所有值连接起来并重新整形。

if (a[-1] == 1) | (a[-1] == 2):
pen = np.concatenate((
starts, first, second, third, np.array([a.shape[0]-1])
))
else:
pen = np.concatenate((
starts, first, second, third, 
))
np.sort(pen).reshape(-1,2)
array([[ 3,  6],
[10, 10],
[13, 14],
[15, 15],
[16, 16],
[19, 19],
[20, 20],
[21, 21]])

对于我的答案,这是计算效率最高的解决方案吗?我意识到四个where语句可以与or运算符组合,但希望每个语句都是单独的,以便读者在本文中看到每个结果。但我想知道是否有一个计算效率更高的解决方案,因为我还没有掌握numpy的所有函数,也不确定每个函数的计算效率。

这类问题的标准技巧是适当地填充输入。在这种情况下,将0附加到数组的末尾是有帮助的:

In [55]: a1 = np.concatenate((a, [0]))
In [56]: a1
Out[56]: 
array([0, 0, 0, 1, 2, 2, 2, 0, 0, 0, 1, 0, 0, 1, 2, 1, 1, 0, 0, 1, 1, 1,
0])

那么你的starts计算仍然有效:

In [57]: starts = np.where(a1 == 1)[0]
In [58]: starts
Out[58]: array([ 3, 10, 13, 15, 16, 19, 20, 21])

结束的条件是该值是12,后面跟着一个不是2的值。你已经想好了处理";然后是";条件下,可以使用数组的移位版本。为了实现andor条件,分别使用逐位二进制运算符&|。在代码中,它看起来像:

In [61]: ends = np.where((a1[:-1] != 0) & (a1[1:] != 2))[0]
In [62]: ends
Out[62]: array([ 6, 10, 14, 15, 16, 19, 20, 21])

最后,将startsends放在一个单独的数组中:

In [63]: np.column_stack((starts, ends))
Out[63]: 
array([[ 3,  6],
[10, 10],
[13, 14],
[15, 15],
[16, 16],
[19, 19],
[20, 20],
[21, 21]])

最新更新