【问题标题】:How to correctly merge overlapping datetime ranges in Python如何在 Python 中正确合并重叠的日期时间范围
【发布时间】:2016-04-20 05:56:25
【问题描述】:

我正在尝试合并重叠的日期时间范围。我将日期时间范围列表作为列表中的元组:

data = [(datetime.datetime(2016, 1, 10, 13, 0), datetime.datetime(2016, 1, 10, 16, 0)), (datetime.datetime(2016, 1, 10, 14, 0), datetime.datetime(2016, 1, 10, 14, 0)), (datetime.datetime(2016, 1, 10, 22, 0), datetime.datetime(2016, 1, 10, 22, 0)), (datetime.datetime(2016, 1, 10, 23, 0), datetime.datetime(2016, 1, 11, 0, 30)), (datetime.datetime(2016, 1, 11, 2, 30), datetime.datetime(2016, 1, 11, 3, 30)), (datetime.datetime(2016, 1, 11, 13, 0), datetime.datetime(2016, 1, 11, 16, 0)), (datetime.datetime(2016, 1, 11, 14, 0), datetime.datetime(2016, 1, 11, 14, 0)), (datetime.datetime(2016, 1, 11, 20, 30), datetime.datetime(2016, 1, 11, 21, 30)), (datetime.datetime(2016, 1, 11, 22, 0), datetime.datetime(2016, 1, 11, 22, 0)), (datetime.datetime(2016, 1, 12, 2, 30), datetime.datetime(2016, 1, 12, 3, 30)), (datetime.datetime(2016, 1, 12, 13, 0), datetime.datetime(2016, 1, 12, 16, 0)), (datetime.datetime(2016, 1, 12, 14, 0), datetime.datetime(2016, 1, 12, 14, 0)), (datetime.datetime(2016, 1, 12, 19, 30), datetime.datetime(2016, 1, 12, 20, 30)), (datetime.datetime(2016, 1, 12, 22, 0), datetime.datetime(2016, 1, 12, 22, 0)), (datetime.datetime(2016, 1, 13, 2, 30), datetime.datetime(2016, 1, 13, 3, 30)), (datetime.datetime(2016, 1, 13, 13, 0), datetime.datetime(2016, 1, 13, 16, 0)), (datetime.datetime(2016, 1, 13, 14, 0), datetime.datetime(2016, 1, 13, 14, 0)), (datetime.datetime(2016, 1, 13, 20, 0), datetime.datetime(2016, 1, 13, 21, 0)), (datetime.datetime(2016, 1, 13, 21, 30), datetime.datetime(2016, 1, 13, 22, 0)), (datetime.datetime(2016, 1, 13, 22, 0), datetime.datetime(2016, 1, 13, 22, 0)), (datetime.datetime(2016, 1, 14, 2, 30), datetime.datetime(2016, 1, 14, 3, 30)), (datetime.datetime(2016, 1, 14, 13, 0), datetime.datetime(2016, 1, 14, 16, 0)), (datetime.datetime(2016, 1, 14, 14, 0), datetime.datetime(2016, 1, 14, 14, 0)), (datetime.datetime(2016, 1, 14, 22, 0), datetime.datetime(2016, 1, 14, 22, 0)), (datetime.datetime(2016, 1, 14, 22, 0), datetime.datetime(2016, 1, 14, 23, 0)), (datetime.datetime(2016, 1, 15, 2, 30), datetime.datetime(2016, 1, 15, 3, 30)), (datetime.datetime(2016, 1, 15, 13, 0), datetime.datetime(2016, 1, 15, 16, 0)), (datetime.datetime(2016, 1, 15, 14, 0), datetime.datetime(2016, 1, 15, 14, 0)), (datetime.datetime(2016, 1, 15, 20, 30), datetime.datetime(2016, 1, 15, 22, 0)), (datetime.datetime(2016, 1, 15, 22, 0), datetime.datetime(2016, 1, 15, 22, 0)), (datetime.datetime(2016, 1, 16, 2, 30), datetime.datetime(2016, 1, 16, 3, 30)), (datetime.datetime(2016, 1, 16, 13, 0), datetime.datetime(2016, 1, 16, 16, 0)), (datetime.datetime(2016, 1, 17, 2, 30), datetime.datetime(2016, 1, 17, 3, 30))]

这是我当前的代码:

import datetime

def merge_date_ranges(data):

    result = []

    for t1, t2 in ((data[i], data[i+1]) for i in range(len(data)-1)):   
        if t1[1] >= t2[0]:
            result.append((min(t1[0], t2[0]), max(t1[1], t2[1])))
        else:
            result.append(t1)

如果 T1(第一个日期时间范围)和 T2(第二个日期时间范围)不重叠,那么我只需将 T1 添加到新列表中(结果)。如果 T1 和 T2 DO 重叠,那么我将合并的元组添加到新列表中(结果)。

我的问题是合并后会发生什么。例如:

T1 = (datetime.datetime(2016, 1, 10, 13, 0), datetime.datetime(2016, 1, 10, 16, 0))
T2 = (datetime.datetime(2016, 1, 10, 14, 0), datetime.datetime(2016, 1, 10, 14, 0))

T1 和 T2 合并,以下添加到我的新列表中:

(datetime.datetime(2016, 1, 10, 13, 0), datetime.datetime(2016, 1, 10, 16, 0))

所以现在我希望我的代码(在 for 循环的下一次迭代中)将合并的元组(新 T1)与我列表中的下一个日期时间范围进行比较:

T1 = (datetime.datetime(2016, 1, 10, 13, 0), datetime.datetime(2016, 1, 10, 16, 0))
T2 = (datetime.datetime(2016, 1, 10, 22, 0), datetime.datetime(2016, 1, 10, 22, 0))

但是,T1 和 T2 是这样的:

T1 = (datetime.datetime(2016, 1, 10, 14, 0), datetime.datetime(2016, 1, 10, 14, 0))
T2 = (datetime.datetime(2016, 1, 10, 22, 0), datetime.datetime(2016, 1, 10, 22, 0))

并且 T1 被添加到我的新列表中(我不想要),因为它之前已经合并了。

但我就是想不通如何做到这一点。如果我能够通过用合并的元组替换 T2 并删除 T1 来更新我的原始列表会更容易。但据我所知,这是不可能的,甚至是一种好的做法。

在拔掉头发一周后,我在这里发布我的第一个问题,希望有人可以帮助我恢复理智。 :)

更新 基本上我想得到一个没有日期时间范围重叠的新列表。

【问题讨论】:

标签: python list datetime merge tuples


【解决方案1】:

我想这就是你想要的。 给它一个测试和评论:

def merge_date_ranges(data):
    result = []
    t_old = data[0]
    for t in data[1:]:
        if t_old[1] >= t[0]:  #I assume that the data is sorted already
            t_old = ((min(t_old[0], t[0]), max(t_old[1], t[1])))
        else:
            result.append(t_old)
            t_old = t
    else:
        result.append(t_old)
    return result

我假设日期已经排序。

顺便说一句,我发现唯一奇怪的日期是单日,也许你应该改正你的输入数据。

salida = merge_date_ranges(data)
for item in [t for t in data if t not in salida]:
    print item

(datetime.datetime(2016, 1, 10, 14, 0), datetime.datetime(2016, 1, 10, 14, 0))
(datetime.datetime(2016, 1, 11, 14, 0), datetime.datetime(2016, 1, 11, 14, 0))
(datetime.datetime(2016, 1, 12, 14, 0), datetime.datetime(2016, 1, 12, 14, 0))
(datetime.datetime(2016, 1, 13, 14, 0), datetime.datetime(2016, 1, 13, 14, 0))
(datetime.datetime(2016, 1, 13, 22, 0), datetime.datetime(2016, 1, 13, 22, 0))
(datetime.datetime(2016, 1, 14, 14, 0), datetime.datetime(2016, 1, 14, 14, 0))
(datetime.datetime(2016, 1, 14, 22, 0), datetime.datetime(2016, 1, 14, 22, 0))
(datetime.datetime(2016, 1, 15, 14, 0), datetime.datetime(2016, 1, 15, 14, 0))
(datetime.datetime(2016, 1, 15, 22, 0), datetime.datetime(2016, 1, 15, 22, 0))

【讨论】:

  • 谢谢!这正是我所需要的。你是明星!
  • 谢谢。我实际上将这些日期添加到列表中以设置每天的开始和结束时间。对我来说,下一步是排除该范围之外的任何日期时间范围。例如,如果我的开始时间是 (14, 0),结束时间是 (22,0),那么我将排除 (9,0) 到 (10,0) 作为示例。
【解决方案2】:

编辑:鉴于新信息,您仍应执行循环,但基于布尔值并检查重叠。

import datetime

def merge_date_ranges(data):

    input = data
    result = []
    overlap = True # we assume there's overlap to begin with

    while(overlap):
        overlap = False # will remain false unless overlap is found

        for t1, t2 in ((input[i], input[i+1]) for i in range(len(input)-1)):   
            if t1[1] >= t2[0]:
                overlap = True # an overlap was found, so loop will continue
                result.append((min(t1[0], t2[0]), max(t1[1], t2[1])))
            else:
                result.append(t1)

        if(overlap):
            input = result # preparing the next round

    return result

【讨论】:

  • 谢谢,但这不是我需要的。如果我没有更清楚,我很抱歉。基本上我想要的是得到一个没有日期时间范围重叠的新列表。
  • 编辑答案以反映新信息,这对您的需要更有意义吗?
猜你喜欢
  • 1970-01-01
  • 2018-03-03
  • 2021-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-10
  • 1970-01-01
相关资源
最近更新 更多