簡體   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