Эффективно найдите, к какому диапазону относится значение

#python

Вопрос:

У меня есть несколько диапазонов даты и времени, связанных со значениями. Я полагаю, что проблема будет такой же для других диапазонов, таких как целые числа.

 ranges = [
    (datetime.datetime(2021, 6, 10, 10, 0), datetime.datetime(2021, 6, 10, 10, 30), 100),
    (datetime.datetime(2021, 6, 10, 10, 30), datetime.datetime(2021, 6, 10, 11, 0), 200),
    (datetime.datetime(2021, 6, 10, 11, 0), datetime.datetime(2021, 6, 10, 11, 30), 150),
    ...
]
 

Этот список отсортирован и не содержит пробелов или перекрытий. Нижняя граница является всеобъемлющей, а верхняя-исключительной.

Для заданной даты и времени я хотел бы найти значение диапазона, к которому он принадлежит:

 def get_value_for_datetime(dt: datetime.datetime) -> int:
    ???
 

Например:

 >>> get_value_for_datetime(datetime.datetime(2021, 6, 10, 10, 45)
>>> 200
 

Моей первой мыслью было взглянуть на bisect модуль, но, похоже, здесь нет ничего, что могло бы помочь мне с диапазонами, и нет способа предоставить пользовательскую функцию для принятия решения смотреть влево или вправо, но, возможно, я что-то упускаю.

Я также не прочь использовать решение numpy и/или pandas, если оно значительно быстрее, а также можно использовать другую структуру для ranges списка, если это поможет.

Комментарии:

1. Интервалы могут быть любой длины.

Ответ №1:

Вы можете использовать деление пополам на нижних границах, а затем проверить, удовлетворяет ли соответствующая верхняя граница условию upper_bound > value :

 import bisect

lb, ub, values = zip(*ranges)

def get_value_for_datetime(x):
    index = bisect.bisect_right(lb, x) - 1
    if index == -1 or ub[index] <= x:
        raise ValueError(x)
    return values[index]