У меня есть два диапазона дат, где каждый диапазон определяется датой начала и окончания (очевидно, экземплярами datetime.date()). Два диапазона могут перекрываться или нет. Мне нужно количество дней перекрытия. Конечно, я могу предварительно заполнить два набора всеми датами в обоих диапазонах и выполнить набор пересечений, но это, возможно, неэффективно... есть ли лучший способ отличить от другого решения, используя длинную секцию if-elif, охватывающую все случаи?
Эффективный расчет перекрытия диапазона дат в python?
Ответ 1
- Определите самую последнюю из двух дат начала и самую раннюю из двух дат окончания.
- Вычислить временную дельту, вычитая их.
- Если дельта положительна, то это число дней перекрытия.
Вот пример расчета:
>>> from datetime import datetime
>>> from collections import namedtuple
>>> Range = namedtuple('Range', ['start', 'end'])
>>> r1 = Range(start=datetime(2012, 1, 15), end=datetime(2012, 5, 10))
>>> r2 = Range(start=datetime(2012, 3, 20), end=datetime(2012, 9, 15))
>>> latest_start = max(r1.start, r2.start)
>>> earliest_end = min(r1.end, r2.end)
>>> delta = (earliest_end - latest_start).days + 1
>>> overlap = max(0, delta)
>>> overlap
52
Ответ 2
Вызов функций дороже, чем арифметические операции.
Самый быстрый способ сделать это включает в себя 2 вычитания и 1 мин():
min(r1.end - r2.start, r2.end - r1.start).days + 1
по сравнению со следующим лучшим, что требует 1 вычитания, 1 мин() и max():
(min(r1.end, r2.end) - max(r1.start, r2.start)).days + 1
Конечно, с обоими выражениями вам все равно нужно проверить положительное перекрытие.
Ответ 3
Я реализовал класс TimeRange, как вы можете видеть ниже.
Метод get_overlapped_range сначала отменяет все неперекрывающиеся параметры простым условием, а затем вычисляет перекрывающийся диапазон с учетом всех возможных опций.
Чтобы получить количество дней, вам нужно взять значение TimeRange, которое было возвращено из get_overlapped_range, и разделить длительность на 60 * 60 * 24.
class TimeRange(object):
def __init__(self, start, end):
self.start = start
self.end = end
self.duration = self.end - self.start
def is_overlapped(self, time_range):
if max(self.start, time_range.start) < min(self.end, time_range.end):
return True
else:
return False
def get_overlapped_range(self, time_range):
if not self.is_overlapped(time_range):
return
if time_range.start >= self.start:
if self.end >= time_range.end:
return TimeRange(time_range.start, time_range.end)
else:
return TimeRange(time_range.start, self.end)
elif time_range.start < self.start:
if time_range.end >= self.end:
return TimeRange(self.start, self.end)
else:
return TimeRange(self.start, time_range.end)
def __repr__(self):
return '{0} ------> {1}'.format(*[time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(d))
for d in [self.start, self.end]])
Ответ 4
псевдокод:
1 + max( -1, min( a.dateEnd, b.dateEnd) - max( a.dateStart, b.dateStart) )
Ответ 5
def get_overlap(r1,r2):
latest_start=max(r1[0],r2[0])
earliest_end=min(r1[1],r2[1])
delta=(earliest_end-latest_start).days
if delta>0:
return delta+1
else:
return 0
Ответ 6
Хорошо, мое решение немного шатко, потому что мой df использует все серии - но допустим, у вас есть следующие столбцы, 2 из которых являются фиксированными, и это ваш "финансовый год". PoP - это "Период выполнения", который представляет собой ваши переменные данные:
df['PoP_Start']
df['PoP_End']
df['FY19_Start'] = '10/1/2018'
df['FY19_End'] = '09/30/2019'
Предположим, что все данные представлены в формате datetime, т.е. -
df['FY19_Start'] = pd.to_datetime(df['FY19_Start'])
df['FY19_End'] = pd.to_datetime(df['FY19_End'])
Попробуйте следующие уравнения, чтобы определить количество перекрывающихся дней:
min1 = np.minimum(df['POP_End'], df['FY19_End'])
max2 = np.maximum(df['POP_Start'], df['FY19_Start'])
df['Overlap_2019'] = (min1 - max2) / np.timedelta64(1, 'D')
df['Overlap_2019'] = np.maximum(df['Overlap_2019']+1,0)