如何有效确定给定值所在的间隔?

时间:2018-08-10 14:12:57

标签: python numpy

为值x考虑以下区域定义:

  • 'A':x <1
  • 'B':1 <= x <10
  • 'C':10 <= x <112
  • 'D':112 <= x

我正在寻找一种有效的方法来确定给定值x的区域。我想出了:

borders = (1, 10, 112)

tst_values = (.2, 2, 22, 222)
for x in tst_values:
    z = next((i for b, i in zip(borders, 'ABC') if x < b), 'D')
    print(f" * Value {x:3g} is in zone '{z}'.")

# The output is:
# * Value 0.2 is in zone 'A'.
# * Value   2 is in zone 'B'.
# * Value  22 is in zone 'C'.
# * Value 222 is in zone 'D'.

解决此问题的最佳实践是什么,尤其是在区域数(即len(borders)大)的情况下。 borders可以包含(增加)浮点数的任意列表。

更新 改写问题以解决评论。

3 个答案:

答案 0 :(得分:3)

一种有效的方法是对边界进行二进制搜索。 Python在bisect库中有一个内置程序,可以为您完成此操作:

import bisect

borders = (1, 10, 112)
tst_values = (.2, 2, 22, 222)
    for x in tst_values:
    border_right = bisect.bisect_right(borders, x) # x < b
    # or
    border_left = bisect.bisect_left(borders, x)   # x > b

这将为您提供x值落入边界的索引。然后,您可以根据需要将该索引转换为字母。

答案 1 :(得分:1)

要有效地找到范围很长的边界列表,我建议使用binary search。基本上,您在边界数组的中间看,检查是否大/小。取决于此,您检查边框数组长度的四分之三处的值。等等。

这可以通过手工递归轻松实现。另外,您可以使用YSelf指出的numpy的实现numpy.digitize。后者几乎肯定会比前者快。

使用numpy.digitize时,您的代码将变为:

borders = (1, 10, 112)
values = (.2, 2, 22, 222)

for x in values:
    z = 'ABCD'[numpy.digitize(x, borders)]
    print(f" * Value {x:3g} is in zone '{z}'.")

答案 2 :(得分:1)

如果您想要有效的方法来解决此问题,请使用np.greater_thannp.lessouter一起使用numpy向量化运算,然后使用np.take取得索引值

a = np.greater_equal.outer(tst_values, [-np.inf, 1,10,112]) & \
    np.less.outer(tst_values, [1, 10,112, np.inf])

np.take(list('ABCD'), np.argmax(a, axis=1))

这非常有效。对于5毫米的行,它的运行时间不到一秒

%timeit (...)
922 ms ± 8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)