Как найти в списке наиболее близкое значение Python?
Есть список -
a = ['2020-07-30', '2020-07-31', '2020-07-31', '2020-07-31', '2020-08-04', '2020-08-05', '2020-08-06', '2020-08-06', '2020-08-08', '2020-08-08']
И есть 2 переменные
b = '2020-07-27'
c = '2020-08-07'
Надо в переменную b1 записать самое близкое значение к переменной b из списка,
то есть - 2020-07-30 либо 2020-08-08 не важно.
А в переменную c1 записать самое близкое значение к переменной c из списка, то есть - 2020-08-06 либо 2020-08-08 не важно.
Ответы (4 шт):
from datetime import date
a = [
'2020-07-30',
'2020-07-31',
'2020-07-31',
'2020-07-31',
'2020-08-04',
'2020-08-05',
'2020-08-06',
'2020-08-06',
'2020-08-08',
'2020-08-08'
]
b = '2020-07-27'
c = '2020-08-07'
a1 = [*map(date.fromisoformat, a)]
b1 = min(a1, key=lambda x: abs(x - date.fromisoformat(b)).days)
c1 = min(a1, key=lambda x: abs(x - date.fromisoformat(c)).days)
print(b1, c1)
# 2020-07-30 2020-08-06
UPD Вариант с проверкой является ли строка датой
from datetime import date
from typing import List, Union
def check_date(date_val: str) -> Union[date, None]:
try:
return date.fromisoformat(date_val)
except ValueError:
return
def nearest_date(date_val: str, dates: List[str]) -> date:
date_list: List[date] = [d for d in map(check_date, dates) if d]
date_v = check_date(date_val)
return min(date_list, key=lambda x: abs((date_v - x).days))
a = [
'2020-07-30',
'2020-07-31',
'2020-07-31',
'2020-07-31',
'2020-08-04',
'2020-08-05',
'2020-08-06',
'2020-08-06',
'2020-08-08',
'2020-08-08'
]
b = '2020-07-27'
c = '2020-08-07'
print(nearest_date(b, a), nearest_date(c, a))
In [38]: import bisect
In [39]: import datetime
In [40]: a = ['2020-07-30', '2020-07-31', '2020-07-31', '2020-07-31', '2020-08-04', '2020-08-05', '2
...: 020-08-06', '2020-08-06', '2020-08-08', '2020-08-08']
In [41]: a = list(map(datetime.date.fromisoformat, a))
In [42]: a.sort()
In [43]: b = '2020-07-27'
In [44]: c = '2020-08-07'
In [45]: c1 = a[bisect.bisect_left(a, datetime.date.fromisoformat(c))]
In [46]: b1 = a[bisect.bisect_left(a, datetime.date.fromisoformat(b))]
In [47]: print(c1, b1)
2020-08-08 2020-07-30
bisect ищет подходящее место для b и c в a но чтобы при этом сохранился порядок a.
Я предполагаю, что переменные a, b, c уже имеют присвоены значения (например такие, как в вашем вопросе).
from datetime import datetime
fmt = "%Y-%m-%d" # год-месяц-день
a_dates = [datetime.strptime(d, fmt) for d in a]
b_date = datetime.strptime(b, fmt)
c_date = datetime.strptime(c, fmt)
b_diff = [abs(b_date - a) for a in a_dates]
c_diff = [abs(c_date - a) for a in a_dates]
b_min = min(zip(b_diff, a_dates))
c_min = min(zip(c_diff, a_dates))
b1 = b_min[1].strftime(fmt)
c1 = c_min[1].strftime(fmt)
Объяснение:
Сначала всё переводим в формат
datetime.datetimeприменением метода.strptime().Формат
datetime.datetimeуже позволяет сделать разности между двумя датами — сделаем их (в абсолютных значениях, в форме генераторов списка).Вычислим минимальные значения разниц — но чтобы знать, к какой дате соответствуют очередная разница, сделаем это на парах
(разница, дата). (Пары получим применением стандартной функцииzip()).Из полученного минимального значения-пары вытащим второй элемент, т.е. дату (второй элемент имеет индекс
[1]) и применением метода.strftime()конвертируем его обратно в строку.
Да почему же все пытаются как-то усложниться, когда все на поверхности - решение в две строки:
date = np.array(a, dtype=np.datetime64)
b1=a[np.argmin(np.abs(date-np.array(b,dtype=np.datetime64)))]
Пример(ы) - привел на другом форуме.