2つの日付範囲があり、各範囲は開始日と終了日によって決定されます(明らかに、datetime.date()インスタンス)。2つの範囲は重複する場合と重複しない場合があります。重なりの日数が必要です。もちろん、両方の範囲内のすべての日付で2つのセットを事前に入力し、セットの共通部分を実行することはできますが、これはおそらく非効率的です...すべてのケースをカバーする長いif-elifセクションを使用する別のソリューションとは別のより良い方法はありますか?
2つの日付範囲があり、各範囲は開始日と終了日によって決定されます(明らかに、datetime.date()インスタンス)。2つの範囲は重複する場合と重複しない場合があります。重なりの日数が必要です。もちろん、両方の範囲内のすべての日付で2つのセットを事前に入力し、セットの共通部分を実行することはできますが、これはおそらく非効率的です...すべてのケースをカバーする長いif-elifセクションを使用する別のソリューションとは別のより良い方法はありますか?
回答:
計算例は次のとおりです。
>>> 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
r1 = Range(start=datetime(2012, 1, 1), end=datetime(2012, 1, 4)); r2 = Range(start=datetime(2012, 1, 2), end=datetime(2012, 1, 3))
。+1
オーバーラップ計算でを逃したと思います(間隔が両端で閉じているために必要です)。
関数呼び出しは、算術演算よりもコストがかかります。
これを行う最も速い方法は、2つの減算と1つのmin()を含みます。
min(r1.end - r2.start, r2.end - r1.start).days + 1
1つの減算、1つのmin()およびmax()を必要とする次善の策と比較して:
(min(r1.end, r2.end) - max(r1.start, r2.start)).days + 1
もちろん、両方の式で、正の重なりをチェックする必要があります。
Range = namedtuple('Range', ['start', 'end']) r1 = Range(start=datetime(2016, 6, 15), end=datetime(2016, 6, 15)) r2 = Range(start=datetime(2016, 6, 11), end=datetime(2016, 6, 18)) print min(r1.end - r2.start, r2.end - r1.start).days + 1
を印刷することになっている場所に4を印刷します
以下に示すように、TimeRangeクラスを実装しました。
get_overlapped_rangeは、最初に単純な条件によってすべての重複していないオプションを無効にし、次にすべての可能なオプションを考慮して重複範囲を計算します。
日数を取得するには、get_overlapped_rangeから返されたTimeRange値を取得し、期間を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]])
datetimerangeパッケージを使用できます:https://pypi.org/project/DateTimeRange/
from datetimerange import DateTimeRange
time_range1 = DateTimeRange("2015-01-01T00:00:00+0900", "2015-01-04T00:20:00+0900")
time_range2 = DateTimeRange("2015-01-01T00:00:10+0900", "2015-01-04T00:20:00+0900")
tem3 = time_range1.intersection(time_range2)
if tem3.NOT_A_TIME_STR == 'NaT': # No overlap
S_Time = 0
else: # Output the overlap seconds
S_Time = tem3.timedelta.total_seconds()
DateTimeRange()内の「2015-01-01T00:00:00 + 0900」は、Timestamp( '2017-08-30 20:36:25')のように日時形式にすることもできます。
DateTimeRange
パッケージのドキュメントを見てみたところ、is_intersection
2つの日付範囲の間に交差があるかどうかに応じて、ブール値(TrueまたはFalse)をネイティブに返すものをサポートしているようです。だから、あなたの例のために:それらが他の場所で交差する場合にtime_range1.is_intersection(time_range2)
戻るだろうTrue
False
擬似コード:
1 + max( -1, min( a.dateEnd, b.dateEnd) - max( a.dateStart, b.dateStart) )
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
私のdfはすべてのシリーズを使用しているため、私のソリューションは少し不安定ですが、次の列があり、そのうちの2つが固定されているとしましょう。これが「会計年度」です。PoPは、変数データである「パフォーマンスの期間」です。
df['PoP_Start']
df['PoP_End']
df['FY19_Start'] = '10/1/2018'
df['FY19_End'] = '09/30/2019'
すべてのデータが日時形式であると想定します。
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)