web-dev-qa-db-fra.com

Calcul efficace du chevauchement des plages de dates en python?

J'ai deux plages de dates où chaque plage est déterminée par une date de début et de fin (évidemment, les instances datetime.date ()). Les deux plages peuvent se chevaucher ou non. J'ai besoin du nombre de jours de chevauchement. Bien sûr, je peux pré-remplir deux ensembles avec toutes les dates dans les deux plages et effectuer une intersection d'ensemble, mais cela est peut-être inefficace ... y a-t-il un meilleur moyen en dehors d'une autre solution en utilisant une longue section if-Elif couvrant tous les cas?

70
Andreas Jung
  • Déterminez la dernière des deux dates de début et la première des deux dates de fin.
  • Calculez le timedelta en les soustrayant.
  • Si le delta est positif, c'est le nombre de jours de chevauchement.

Voici un exemple de calcul:

>>> 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
150
Raymond Hettinger

Les appels de fonction sont plus chers que les opérations arithmétiques.

Le moyen le plus rapide de le faire implique 2 soustractions et 1 min ():

min(r1.end - r2.start, r2.end - r1.start).days + 1

par rapport au meilleur suivant qui nécessite 1 soustraction, 1 min () et un max ():

(min(r1.end, r2.end) - max(r1.start, r2.start)).days + 1

Bien sûr, avec les deux expressions, vous devez toujours vérifier un chevauchement positif.

8
John Machin

J'ai implémenté une classe TimeRange comme vous pouvez le voir ci-dessous.

Get_overlapped_range annule d'abord toutes les options non superposées par une simple condition, puis calcule la plage superposée en considérant toutes les options possibles.

Pour obtenir le nombre de jours, vous devrez prendre la valeur TimeRange renvoyée par get_overlapped_range et diviser la durée par 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]])
5
Elad Sofer

Pseudocode:

 1 + max( -1, min( a.dateEnd, b.dateEnd) - max( a.dateStart, b.dateStart) )
3
ypercubeᵀᴹ
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
0
andros1337