繁体   English   中英

如何有效地确定给定值的区间?

[英]How to efficiently determine the interval a given value is in?

考虑值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可能包含任意(增加)浮点数列表。

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

一种有效的方法是通过边界进行二进制搜索。 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落入的边界的索引。 如果您愿意,可以将该索引转换为字母。

为了有效地找到你在一长串排序边界中的范围,我建议使用二进制搜索 基本上,你看看你的边界数组的中间,检查你是否大/小。 根据具体情况,您可以检查边界阵列长度的一/四分之三的值。 等等。

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

使用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}'.")

如果你想有效的方法来解决此问题,使用使用numpy的矢量运算np.greater_thannp.lessouter ,再服用指数值与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 MM行,它在不到一秒的时间内运行

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM