如何在两个不同的列表中“减去”重叠的元组时间范围

时间:2019-04-03 21:48:09

标签: python

我有两个元组列表,每个元组都由开始时间和结束时间(从纪元开始的秒数)组成,如下所示:

list1= [(2,4), (7,10), (14,22)]

list2 = [(1,3), (5,8), (9,15), (20,24)]

我需要创建一个新的元组列表(开始,结束),以删除list2中的元组与list1中的元组重叠的时间间隔。

在给定list1list2的情况下,该方法的预期输出为:

[(3,4), (8,10), (15,20)]

对于价值而言,list1list2都将包含唯一的时间范围,即每个单独的列表内都不会重叠。

1 个答案:

答案 0 :(得分:2)

这是一个仅在列表的每个元素上迭代一次的解决方案。

有了list1 = [(2, 4), ...]list2 = [(1, 3), ...],我们有了:

  • 从1开始,已删除的部分
  • 在2点开始间隔
  • 在3处,已删除部分的结尾
  • 在4点间隔结束

输出由我们都位于区间内而不是被删除的部分组成。

因此,我们的想法是按顺序浏览事件,跟踪我们是否处于间隔内以及是否位于已删除的部分内。

我们首先从每个列表创建一个生成器,该生成器将生成:

  • 对于第一个,Event(pos=2, toggle='in_interval')Event(pos=4, toggle='in_interval'),依此类推。
  • 第二个是Event(pos=1, toggle='in_deleted')Event(pos=3, toggle='in_deleted') ...

我们不必关心每个值是间隔的开始还是结束,因为它只是切换状态(间隔的内部/外部)。

然后我们可以使用heapq.merge从这两个生成器中按顺序获取事件,这将为我们提供类似的信息:

Event(pos=1, toggle='in_deleted')Event(pos=2, toggle='in_interval')Event(pos=3, toggle='in_deleted')Event(pos=4, toggle='in_interval') ...

每个事件将切换关联的状态。当我们既处于间隔中又不在删除的部分中时,我们将为输出创建一个新的间隔。其余的不言而喻...

from heapq import merge
from itertools import chain
from collections import namedtuple

def remaining(intervals, deleted):
    Event = namedtuple('Event', ['position', 'toggle'])

    int_iter = (Event(position=pos, toggle='in_interval') for pos in chain.from_iterable(intervals))
    del_iter = (Event(position=pos, toggle='in_deleted') for pos in chain.from_iterable(deleted))

    state = {'in_interval': False, 'in_deleted': False}
    start = None
    out = []

    for event in merge(int_iter, del_iter):
        state[event.toggle] = not state[event.toggle]
        if state['in_interval'] and not state['in_deleted']:
            # start a new interval
            start = event.position
        elif start is not None:
            # end an interval. If it's not empty, we append it to the output
            if event.position > start:
                out.append((start, event.position))
            start = None
    return out

示例输出:

list1 = [(2,4), (7,10), (14,22)]
list2 = [(1,3), (5,8), (9,15), (20,24)]      

print(remaining(list1, list2))
# [(3, 4), (8, 9), (15, 20)]