为值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
可以包含(增加)浮点数的任意列表。
更新 改写问题以解决评论。
答案 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_than
和np.less
与outer
一起使用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)