2014-12-05 160 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和可讀。