如何将两个日期列表合并为一系列日期间隔?

时间:2012-08-24 03:23:38

标签: python list date merge

我有开始日期列表和结束日期列表。他们排序......

start_dates = [
    datetime.date(2009, 11, 5), datetime.date(2009, 11, 13), 
    datetime.date(2009, 11, 25), datetime.date(2009, 11, 26), 
    datetime.date(2009, 12, 4), datetime.date(2009, 12, 7), 
    datetime.date(2009, 12, 29), datetime.date(2009, 12, 30)]

end_dates = [
    datetime.date(2009, 10, 1), datetime.date(2009, 10, 2), 
    datetime.date(2009, 10, 9), datetime.date(2009, 10, 12), 
    datetime.date(2009, 11, 4), datetime.date(2009, 12, 14), 
    datetime.date(2009, 12, 15)]

开始日期代表我们收到购买股票的建议的日期。结束日期是我们收到销售建议的日期。建议的来源是不同的,我们正在回溯测试如果我们使用来自一个来源的购买建议会发生什么,但是来自另一个来源的销售建议。因此,我们有两个日期序列,我们希望将它们分解为成对或间隔 - 我们将在股票中持有头寸。

因此,我们将从start_dates开始确定何时购买股票:11月5日我们买入头寸。然后我们循环查看end_dates,寻找第一个出售它的建议:12月14日。重复一次,当我们从一个来源建议我们不持仓时卖出,当我们从另一个来源持有卖出时卖出源。

你可能会说我们想要在两个我们正在循环的列表之间徘徊。

因此上面的输入生成:

result = (
  (datetime.date(2009, 11, 5), datetime.date(2009, 12, 14)),
  (datetime.date(2009, 12, 29), None)
)

我在for循环中使用for循环,并想知道是否有更好的方法。性能很有意义,因为它将在40年的时间内应用于数千个场景;一些名单涉及数千个日期。

4 个答案:

答案 0 :(得分:2)

修改

这应该与len(start_dates)+len(end_dates)

一起扩展
def date_range(start_dates, end_dates):
    result = []

    start_i = 0
    end_i = 0

    while start_i<len(start_dates):
        while end_i<len(end_dates) and start_dates[start_i]>end_dates[end_i]:
            end_i += 1
        if end_i == len(end_dates):
            result.append((start_dates[start_i], None))
            break
        result.append((start_dates[start_i], end_dates[end_i]))
        while start_i<len(start_dates) and start_dates[start_i]<=end_dates[end_i]:
            start_i += 1
        end_i += 1

    return result

用法:

In  : start_dates = [
   ....:     datetime.date(2009, 11, 5), datetime.date(2009, 11, 13),
   ....:     datetime.date(2009, 11, 25), datetime.date(2009, 11, 26),
   ....:     datetime.date(2009, 12, 4), datetime.date(2009, 12, 7),
   ....:     datetime.date(2009, 12, 29), datetime.date(2009, 12, 30)]

In : end_dates = [
   ....:     datetime.date(2009, 10, 1), datetime.date(2009, 10, 2),
   ....:     datetime.date(2009, 10, 9), datetime.date(2009, 10, 12),
   ....:     datetime.date(2009, 11, 4), datetime.date(2009, 12, 14),
   ....:     datetime.date(2009, 12, 15)]

In : date_range(start_dates, end_dates)
Out:
[(datetime.date(2009, 11, 5), datetime.date(2009, 12, 14)),
 (datetime.date(2009, 12, 29), None)]

In : start_dates = [
   ....:     datetime.date(2009, 11, 5), datetime.date(2009, 11, 13),
   ....:     datetime.date(2009, 11, 25), datetime.date(2009, 11, 26),
   ....:     datetime.date(2009, 12, 4), datetime.date(2009, 12, 7),
   ....:     datetime.date(2009, 12, 29), datetime.date(2009, 12, 30)]

In : end_dates = [
   ....:     datetime.date(2009, 10, 1), datetime.date(2009, 10, 2),
   ....:     datetime.date(2009, 10, 9), datetime.date(2009, 10, 12),
   ....:     datetime.date(2009, 11, 7), datetime.date(2009, 12, 14), # changed (2009, 11, 4) -> (2009, 11, 7)
   ....:     datetime.date(2009, 12, 15)]

In : date_range(start_dates, end_dates)
Out:
[(datetime.date(2009, 11, 5), datetime.date(2009, 11, 7)),
 (datetime.date(2009, 11, 13), datetime.date(2009, 12, 14)),
 (datetime.date(2009, 12, 29), None)]

答案 1 :(得分:1)

我认为这应该可以获得日期元组,但是我不能在不使用for循环的情况下建议你,因为它可能会变得更加复杂。

虽然逻辑非常简单明了。

result = []
for startd in start_dates:
    if not result or result[-1][1] is not None and startd>result[-1][1]:
    #can use 'len(result)==0' instead of 'not result'
        for endd in end_dates:
            if endd>startd:
                result.append((startd,endd))
                break
        else:
            result.append((start,None))
    if result[-1][1] is None:
        break

result = tuple(result)
print result

答案 2 :(得分:1)

我终于把它钉在了:

    trades = []
    enddate = datetime.date(1900, 1, 1)
    try:
        for startdate in startdates:
            if enddate <= startdate:
                enddate = enddates.next()
                while enddate <= startdate:
                    enddate = enddates.next()
                trades.append((startdate, enddate))
    except StopIteration:
        trades.append((startdate, None))

感谢那些提出问题并回答的人。由于没有理性的原因,这个小小的谜题成了我的固定但我想最后我已经做到了这一点,并且应该继续我的生活。最后它真的非常简单 - 令人惊讶的是,要做到这一点,需要做多少工作!

答案 3 :(得分:0)

这个怎么样。

all_dates = start_dates.expand(end_dates)
all_dates.sort()

look_for_start = 1;
date = []
start_date = None
end_date = None
for i in range(len(all_dates)):
   if look_for_start and all_dates[i] in start_dates:
     start_date = all_dates[i]
     look_for_start = 0;

   elsif !look_for_start and all_dates[i] in end_dates:
     end_date = all_dates[1]
     look_for_start = 1;

   if start_date == end_date:
     end_date == None
     look_for_start = 0;

   if start_date != None and end_date != None;
     date.append((start_date,end_date))
     start_date = None
     end_date = None

在此之后,你有尽可能的结束日期的start_dates。只需获取剩余的start_dates集,并将其结束日期设置为None