2014-12-05 320 views
1

如何根据缺失日期将缺失日期的日期时间列表拆分为列表列表?使用Python根据缺失日期拆分日期时间列表

下面的例子:

date_list = [ 
     datetime.datetime(2012,1,1,0,0,0), 
     datetime.datetime(2012,1,2,0,0,0), 
     datetime.datetime(2012,1,4,0,0,0), 
     datetime.datetime(2012,1,7,0,0,0), 
     datetime.datetime(2012,1,8,0,0,0), 
     ] 

结果我找这里是

[[datetime.datetime(2012,1,1,0,0,0), datetime.datetime(2012,1,2,0,0,0)] 
[datetime.datetime(2012,1,4,0,0,0)], 
[datetime.datetime(2012,1,7,0,0,0), datetime.datetime(2012,1,8,0,0,0)]] 

我试着用groupby,但我想不出用什么为重点。

[list(g) for k, g in itertools.groupby(date_list, key=lambda d: d.day)]

+0

您可能会发现第二个例子([旧版本])[itertools文档](https://docs.python.org/2.6/library/itertools.html#examples)有用。另一方面,如果你不关心超级花式,[写你自己的发电机](http://stackoverflow.com/questions/21142231/group-consecutive-integers-and-tolerate-gaps-of-1/ 21142465#21142465)非常简单。 – roippi 2014-12-05 00:18:59

回答

2

这适用于给定的例子...

>>> import datetime 
>>> date_list = [ 
...   datetime.datetime(2012,1,1,0,0,0), 
...   datetime.datetime(2012,1,2,0,0,0), 
...   datetime.datetime(2012,1,4,0,0,0), 
...   datetime.datetime(2012,1,7,0,0,0), 
...   datetime.datetime(2012,1,8,0,0,0), 
...   ] 
>>> import itertools 
>>> [list(g) for k, g in itertools.groupby(enumerate(date_list), key=lambda (i, x): i-x.day)] 
[[(0, datetime.datetime(2012, 1, 1, 0, 0)), (1, datetime.datetime(2012, 1, 2, 0, 0))], [(2, datetime.datetime(2012, 1, 4, 0, 0))], [(3, datetime.datetime(2012, 1, 7, 0, 0)), (4, datetime.datetime(2012, 1, 8, 0, 0))]] 

这可能是更好,如果你不希望指数...

>>> [[v for i, v in g] for k, g in itertools.groupby(enumerate(date_list), key=lambda (i, x): i-x.day)] 
[[datetime.datetime(2012, 1, 1, 0, 0), datetime.datetime(2012, 1, 2, 0, 0)], [datetime.datetime(2012, 1, 4, 0, 0)], [datetime.datetime(2012, 1, 7, 0, 0), datetime.datetime(2012, 1, 8, 0, 0)]] 
1

你可以建立在没有连续日期时可以“切换”的键:

class Switcher(): 
    def __call__(self, d): 
     if not hasattr(self, 'prev'): # first element: init switch 
      self.switch = 1 
     elif (d - self.prev).days > 1: # not consecutive: invert switch 
      self.switch *= -1 
     self.prev = d     # save current value 
     return self.switch 

然后你可以使用它像:

>>> [list(g) for k, g in groupby(date_list, key = Switcher())] 
[[datetime.datetime(2012, 1, 1, 0, 0), datetime.datetime(2012, 1, 2, 0, 0)], 
[datetime.datetime(2012, 1, 4, 0, 0)], 
[datetime.datetime(2012, 1, 7, 0, 0), datetime.datetime(2012, 1, 8, 0, 0)]] 
+0

如果你只使用这个类的'__call__'设施,你为什么不把它变成一个函数?只要删除'class Switcher'行,移动缩进,并将'__call__'改为任何内容,然后计算'zip(date_list [1:],date_list)上的开关情况'......似乎可以这样做只有结果是更少的代码和更少的混淆代码。 – ely 2014-12-05 01:13:11

+0

@ prpl.mnky.dshwshr,原因是为了记住以前的元素/开关状态,关键对象需要有内存('self.prev','self.switch')。一个函数将是无状态的。 – elyase 2014-12-05 01:20:21

+0

这就是为什么我说要从拉链计算开关盒,而不是假装他们是状态。我不是在争论你是否可以用这种方式来表达,只是它不能很好地利用课堂。此外,您可以*在该函数中具有“状态”,可以通过生成一个生成器或使用闭包。 – ely 2014-12-05 01:28:33

2

这是一个无聊的for循环辅助函数来做到这一点。

def date_segments(dates): 
    output = [] 
    cur_list = [dates[0]] 
    for dt_pair in zip(dates[1:], dates): 
     if (dt_pair[0] - dt_pair[1]).days > 1: 
      output.append(cur_list) 
      cur_list = [dt_pair[0]] 
     else: 
      cur_list.append(dt_pair[0]) 
    output.append(cur_list) 
    return output 

这给:

In [28]: date_segments(date_list) 
Out[28]: 
[[datetime.datetime(2012, 1, 1, 0, 0), datetime.datetime(2012, 1, 2, 0, 0)], 
[datetime.datetime(2012, 1, 4, 0, 0)], 
[datetime.datetime(2012, 1, 7, 0, 0), datetime.datetime(2012, 1, 8, 0, 0)]] 

如果我定义了itertools.groupby方法为如下命名other_way一个辅助功能:

from itertools import groupby 
def other_way(date_list): 
    return [[v for i, v in g] for k, g in groupby(enumerate(date_list), 
                key=lambda (i, x): i-x.day)] 

那么对于这个公认的小例子timeit显示了这种换循环方法稍快:

In [31]: %timeit date_segments(date_list) 
100000 loops, best of 3: 3.2 µs per loop 

In [32]: %timeit other_way(date_list) 
100000 loops, best of 3: 3.72 µs per loop 

和我,其中之一,找到forth循环方法更Pythonic和可读。