Get unions and intersections of list of datetime ranges python

后端 未结 2 1026
我寻月下人不归
我寻月下人不归 2021-01-14 08:49

I have two lists of datetime ranges. eg.

l1 = [(datetime.datetime(2018, 8, 29, 1, 0, 0), datetime.datetime(2018, 8, 29, 3, 0, 0)), (datetime.dat         


        
相关标签:
2条回答
  • 2021-01-14 09:01

    Your definition of union and intersection for the date range can be simply described as :-

    Union:

    In []:
    from itertools import product
    [(min(s1, s2), max(e1, e2)) for (s1, e1), (s2, e2) in product(l1, l2) if s1 <= e2 and e1 >= s2]
    
    Out[]:
    [(datetime.datetime(2018, 8, 29, 1, 0), datetime.datetime(2018, 8, 29, 4, 0)),
     (datetime.datetime(2018, 8, 29, 5, 0), datetime.datetime(2018, 8, 29, 9, 0))]
    

    Intersection:

    In []:
    [(max(s1, s2), min(e1, e2)) for (s1, e1), (s2, e2) in product(l1, l2) if s1 <= e2 and e1 >= s2]
    
    Out[]:
    [(datetime.datetime(2018, 8, 29, 2, 0), datetime.datetime(2018, 8, 29, 3, 0)),
     (datetime.datetime(2018, 8, 29, 6, 0), datetime.datetime(2018, 8, 29, 7, 0))]
    

    You can replace <= and >= with < and > if they strictly have to overlap and not just touch.

    0 讨论(0)
  • 2021-01-14 09:16

    The answer here is very useful for what you're asking, as it can compact an array of overlapping ranges:

    from operator import itemgetter
    
    def consolidate(intervals):
        sorted_intervals = sorted(intervals, key=itemgetter(0))
    
        if not sorted_intervals:  # no intervals to merge
            return
    
        # low and high represent the bounds of the current run of merges
        low, high = sorted_intervals[0]
    
        for iv in sorted_intervals[1:]:
            if iv[0] <= high:  # new interval overlaps current run
                high = max(high, iv[1])  # merge with the current run
            else:  # current run is over
                yield low, high  # yield accumulated interval
                low, high = iv  # start new run
    
        yield low, high  # end the final run
    

    Union of l1 and l2 is simply the consolidation of all ranges in both l1 and l2:

    def union(l1, l2):
        return consolidate([*l1, *l2])
    

    Intersection of l1 and l2 is adequately done by AChampion's code (if there is any overlap between any range in l1 and any range in l2, that overlap deserves to be in the result), but it could lead to fragmentation of ranges; we can use this same function to join the adjacent or overlapping ranges:

    from itertools import product
    
    def intersection(l1, l2):
        result = ((max(s1, s2), min(e1, e2)) for (s1, e1), (s2, e2) in product(l1, l2) if s1 < e2 and e1 > s2)
        return consolidate(result)
    

    An example:

    l1 = [(1, 7), (4, 8), (10, 15), (20, 30), (50, 60)]
    l2 = [(3, 6), (8, 11), (15, 20)]
    print(list(union(l1, l2)))         # [(1, 30), (50, 60)]
    print(list(intersection(l1, l2)))  # [(3, 6), (10, 11)]
    

    (The example uses integers for clarity, but it works with any comparable type. Specifically, for OP's l1 and l2, the code yields OP's desired datetime results.)

    0 讨论(0)
提交回复
热议问题