Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/355.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 合并具有重叠时间范围的时间范围元组列表_Python_Algorithm_Merge - Fatal编程技术网

Python 合并具有重叠时间范围的时间范围元组列表

Python 合并具有重叠时间范围的时间范围元组列表,python,algorithm,merge,Python,Algorithm,Merge,我有一个元组列表,其中每个元组都是(开始时间,结束时间)。我正在尝试合并所有重叠的时间范围,并返回不同时间范围的列表。 比如说 [(1, 5), (2, 4), (3, 6)] ---> [(1,6)] [(1, 3), (2, 4), (5, 8)] ---> [(1, 4), (5,8)] 下面是我如何实现它的 # Algorithm # initialranges: [(a,b), (c,d), (e,f), ...] # First we sort each tuple

我有一个元组列表,其中每个元组都是
(开始时间,结束时间)
。我正在尝试合并所有重叠的时间范围,并返回不同时间范围的列表。 比如说

[(1, 5), (2, 4), (3, 6)] --->  [(1,6)]
[(1, 3), (2, 4), (5, 8)] --->  [(1, 4), (5,8)]
下面是我如何实现它的

# Algorithm
# initialranges: [(a,b), (c,d), (e,f), ...]
# First we sort each tuple then whole list.
# This will ensure that a<b, c<d, e<f ... and a < c < e ... 
# BUT the order of b, d, f ... is still random
# Now we have only 3 possibilities
#================================================
# b<c<d: a-------b           Ans: [(a,b),(c,d)]
#                  c---d
# c<=b<d: a-------b          Ans: [(a,d)]
#               c---d
# c<d<b: a-------b           Ans: [(a,b)]
#         c---d
#================================================
def mergeoverlapping(initialranges):
    i = sorted(set([tuple(sorted(x)) for x in initialranges]))

    # initialize final ranges to [(a,b)]
    f = [i[0]]
    for c, d in i[1:]:
        a, b = f[-1]
        if c<=b<d:
            f[-1] = a, d
        elif b<c<d:
            f.append((c,d))
        else:
            # else case included for clarity. Since 
            # we already sorted the tuples and the list
            # only remaining possibility is c<d<b
            # in which case we can silently pass
            pass
    return f
#算法
#初始范围:[(a,b),(c,d),(e,f),…]
#首先我们对每个元组排序,然后对整个列表排序。
#这将确保
如果t1.right>=t2.left=>merge,则对元组排序,然后列出
然后用新列表重新启动

-->
def(l,sort=True):
如果排序:
sl=已排序(元组(已排序(i))表示l中的i)
其他:
sl=l
如果len(sl)>1:
如果sl[0][1]>=sl[1][0]:
sl[0]=(sl[0][0],sl[1][1])
德尔sl[1]
如果len(sl)
让它更高效的几种方法,Pythonic:

  • 消除
    set()
    构造,因为算法应该在主循环中删除重复项
  • 如果只需要迭代结果,请使用
    yield
    生成值
  • 减少中间对象的构造,例如:将
    tuple()
    调用移动到生成最终值的点,这样就不必构造和丢弃额外的tuple,并重用一个列表
    saved
    来存储当前时间范围以供比较
  • 代码:

    def合并(次):
    保存=列表(次数[0])
    对于st,en in排序([排序(t)表示t in times]):
    
    如果st排序部分:使用标准排序,它已经以正确的方式比较元组了

    sorted_tuples = sorted(initial_ranges)
    
    合并部分。它还消除了重复的范围,因此不需要设置
    。假设您有
    当前组
    下一个组

    c_start, c_end = current_tuple
    n_start, n_end = next_tuple
    if n_start <= c_end: 
      merged_tuple = min(c_start, n_start), max(c_end, n_end)
    
    c\u开始,c\u结束=当前元组
    n\u开始,n\u结束=下一个元组
    
    如果n_start对所有边界进行排序,则在边界结束后是边界开始的位置对所有边界进行排序

    def mergeOverlapping(initialranges):
        def allBoundaries():
            for r in initialranges:
                yield r[0], True
                yield r[1], False
    
        def getBoundaries(boundaries):
            yield boundaries[0][0]
            for i in range(1, len(boundaries) - 1):
                if not boundaries[i][1] and boundaries[i + 1][1]:
                    yield boundaries[i][0]
                    yield boundaries[i + 1][0]
            yield boundaries[-1][0]
    
        return getBoundaries(sorted(allBoundaries()))
    
    嗯,没那么漂亮,但至少写起来很有趣

    编辑:几年后,经过一次投票,我意识到我的代码错了!这是新版本,只是为了好玩:

    def mergeOverlapping(initialRanges):
        def allBoundaries():
            for r in initialRanges:
                yield r[0], -1
                yield r[1], 1
    
        def getBoundaries(boundaries):
            openrange = 0
            for value, boundary in boundaries:
                if not openrange:
                    yield value
                openrange += boundary
                if not openrange:
                    yield value
    
        def outputAsRanges(b):
            while b:
                yield (b.next(), b.next())
    
        return outputAsRanges(getBoundaries(sorted(allBoundaries())))
    

    基本上,我用-1或1标记边界,然后按值排序,仅当开括号和闭括号之间的平衡为零时才输出它们。

    很晚了,但可能有助于查找此项。我有一个类似的问题,但字典。给定一个时间范围列表,我希望找到重叠并在可能的情况下合并它们。对@samplebias答案稍加修改后,我得出以下结论:

    合并功能:

    def merge_range(ranges: list, start_key: str, end_key: str):
        ranges = sorted(ranges, key=lambda x: x[start_key])
        saved = dict(ranges[0])
    
        for range_set in sorted(ranges, key=lambda x: x[start_key]):
            if range_set[start_key] <= saved[end_key]:
                saved[end_key] = max(saved[end_key], range_set[end_key])
            else:
                yield dict(saved)
                saved[start_key] = range_set[start_key]
                saved[end_key] = range_set[end_key]
        yield dict(saved)
    
    data = [
        {'start_time': '09:00:00', 'end_time': '11:30:00'},
        {'start_time': '15:00:00', 'end_time': '15:30:00'},
        {'start_time': '11:00:00', 'end_time': '14:30:00'},
        {'start_time': '09:30:00', 'end_time': '14:00:00'}
    ]
    
    print(list(merge_range(ranges=data, start_key='start_time', end_key='end_time')))
    
    [
        {'start_time': '09:00:00', 'end_time': '14:30:00'},
        {'start_time': '15:00:00', 'end_time': '15:30:00'}
    ]
    
    执行:

    def merge_range(ranges: list, start_key: str, end_key: str):
        ranges = sorted(ranges, key=lambda x: x[start_key])
        saved = dict(ranges[0])
    
        for range_set in sorted(ranges, key=lambda x: x[start_key]):
            if range_set[start_key] <= saved[end_key]:
                saved[end_key] = max(saved[end_key], range_set[end_key])
            else:
                yield dict(saved)
                saved[start_key] = range_set[start_key]
                saved[end_key] = range_set[end_key]
        yield dict(saved)
    
    data = [
        {'start_time': '09:00:00', 'end_time': '11:30:00'},
        {'start_time': '15:00:00', 'end_time': '15:30:00'},
        {'start_time': '11:00:00', 'end_time': '14:30:00'},
        {'start_time': '09:30:00', 'end_time': '14:00:00'}
    ]
    
    print(list(merge_range(ranges=data, start_key='start_time', end_key='end_time')))
    
    [
        {'start_time': '09:00:00', 'end_time': '14:30:00'},
        {'start_time': '15:00:00', 'end_time': '15:30:00'}
    ]
    
    输出:

    def merge_range(ranges: list, start_key: str, end_key: str):
        ranges = sorted(ranges, key=lambda x: x[start_key])
        saved = dict(ranges[0])
    
        for range_set in sorted(ranges, key=lambda x: x[start_key]):
            if range_set[start_key] <= saved[end_key]:
                saved[end_key] = max(saved[end_key], range_set[end_key])
            else:
                yield dict(saved)
                saved[start_key] = range_set[start_key]
                saved[end_key] = range_set[end_key]
        yield dict(saved)
    
    data = [
        {'start_time': '09:00:00', 'end_time': '11:30:00'},
        {'start_time': '15:00:00', 'end_time': '15:30:00'},
        {'start_time': '11:00:00', 'end_time': '14:30:00'},
        {'start_time': '09:30:00', 'end_time': '14:00:00'}
    ]
    
    print(list(merge_range(ranges=data, start_key='start_time', end_key='end_time')))
    
    [
        {'start_time': '09:00:00', 'end_time': '14:30:00'},
        {'start_time': '15:00:00', 'end_time': '15:30:00'}
    ]
    

    非常感谢。同意我应该消除
    set()
    。循环处理它。类似于根据需要生成元组而不是附加到列表中的想法。不幸的是,如果
    len(times)==0
    ,这将失败。此外,如果输入列表未排序(例如
    [(3,6),(2,4)]
    ),这将不起作用。保存的
    的初始值必须是排序列表的第一个元素。小心!使用此技术将导致Python3.7+出现问题,因为使用PEP 479时,生成器的行为将发生更改并引发
    运行时错误
    !!!请看,我同意消除
    set()
    。逻辑很清楚。非常感谢。但是我不得不接受@samplebias的答案,而不是这个(尽管这个想法基本上是一样的),因为他是第一个回应的人(他有完整的代码!):)没关系。而且,它看起来有点像家庭作业,所以我给读者留了几个位作为练习:)如果在开始时存在不可合并的范围,例如[(6,11),(13,40),(33,44)],则此算法不起作用。