2012-01-24 11 views
10

Mam posortowaną listę datetimes: (z dniowych przerw)Podział lista datetimes w dniach

list_of_dts = [ 
       datetime.datetime(2012,1,1,0,0,0), 
       datetime.datetime(2012,1,1,1,0,0), 
       datetime.datetime(2012,1,2,0,0,0), 
       datetime.datetime(2012,1,3,0,0,0), 
       datetime.datetime(2012,1,5,0,0,0), 
       ] 

I chciałbym podzielić je do listy na każdy dzień:

result = [ 
      [datetime.datetime(2012,1,1,0,0,0), datetime.datetime(2012,1,1,1,0,0)], 
      [datetime.datetime(2012,1,2,0,0,0)], 
      [datetime.datetime(2012,1,3,0,0,0)], 
      [], # Empty list for no datetimes on day 
      [datetime.datetime(2012,1,5,0,0,0)] 
     ] 

Algorytmicznie, powinno być możliwe osiągnięcie co najmniej O (n).

Może coś jak następujących: (To oczywiście nie obsługuje brakowało dni, a spadnie ostatni dt, ale to dopiero początek)

def dt_to_d(list_of_dts): 
    result = [] 
    start_dt = list_of_dts[0] 
    day = [start_dt] 
    for i, dt in enumerate(list_of_dts[1:]): 
     previous = start_dt if i == 0 else list_of_dts[i-1] 
     if dt.day > previous.day or dt.month > previous.month or dt.year > previous.year: 
      # split to new sub-list 
      result.append(day) 
      day = [] 
      # Loop for each day gap? 
     day.append(dt) 
    return result 

Myśli?

+1

użyć dict list z datetime_value .date() jako klucze. –

Odpowiedz

12

Najprostszym sposobem jest użycie dict.setdefault wpisów grup wchodzących w tym samym dniu, a następnie pętli nad najniższym dnia do najwyższego:

>>> import datetime 
>>> list_of_dts = [ 
       datetime.datetime(2012,1,1,0,0,0), 
       datetime.datetime(2012,1,1,1,0,0), 
       datetime.datetime(2012,1,2,0,0,0), 
       datetime.datetime(2012,1,3,0,0,0), 
       datetime.datetime(2012,1,5,0,0,0), 
       ] 

>>> days = {} 
>>> for dt in list_of_dts: 
     days.setdefault(dt.toordinal(), []).append(dt) 

>>> [days.get(day, []) for day in range(min(days), max(days)+1)] 
[[datetime.datetime(2012, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 1, 0)], 
[datetime.datetime(2012, 1, 2, 0, 0)], 
[datetime.datetime(2012, 1, 3, 0, 0)], 
[], 
[datetime.datetime(2012, 1, 5, 0, 0)]] 

Innym podejściem do tworzenia takich ugrupowań jest itertools.groupby. Jest on przeznaczony do tego rodzaju pracy, ale to nie daje drogę do wypełnienia w pustej listy brakujących dni:

>>> import itertools 
>>> [list(group) for k, group in itertools.groupby(list_of_dts, 
                key=datetime.datetime.toordinal)] 
[[datetime.datetime(2012, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 1, 0)], 
[datetime.datetime(2012, 1, 2, 0, 0)], 
[datetime.datetime(2012, 1, 3, 0, 0)], 
[datetime.datetime(2012, 1, 5, 0, 0)]] 
+1

setdefault i toordinal są miłymi ulepszeniami w stosunku do mojej odpowiedzi. :-) –

+2

Jeszcze prostsze niż 'setdefault' może być' defaultdict' (from 'collections'). – Amber

+0

Piękne - dziękuję! Mogę wygenerować listę 'days' w tym samym czasie, co moja' list_of_dts', więc będzie super wydajna. –

4

Można użyć itertools.groupby łatwo obsługiwać tego rodzaju problemy:

import datetime 
import itertools 

list_of_dts = [ 
     datetime.datetime(2012,1,1,0,0,0), 
     datetime.datetime(2012,1,1,1,0,0), 
     datetime.datetime(2012,1,2,0,0,0), 
     datetime.datetime(2012,1,3,0,0,0), 
     datetime.datetime(2012,1,5,0,0,0), 
     ] 

print [list(g) for k, g in itertools.groupby(list_of_dts, key=lambda d: d.date())] 
+0

Dobrze wiedzieć - dzięki! Nie obsługuje jednak wymogu pustej listy brakujących dni. –

+0

@Alex Ahh, rzeczywiście, jakoś nie zauważyłem tego punktu. – qiao

1

wypełnienie luk:

date_dict = {} 
for date_value in list_of_dates: 
    if date_dict.has_key(date_value.date()): 
     date_dict[date_value.date()].append(date_value) 
    else: 
     date_dict[date_value.date()] = [ date_value ] 
sorted_dates = sorted(date_dict.keys()) 
date = sorted_dates[0] 
while date <= sorted_dates[-1]: 
    print date_dict.get(date, []) 
    date += datetime.timedelta(1) 

Wyniki:

[datetime.datetime(2012, 1, 1, 0, 0), datetime.datetime(2012, 1, 1, 1, 0)] 
[datetime.datetime(2012, 1, 2, 0, 0)] 
[datetime.datetime(2012, 1, 3, 0, 0)] 
[] 
[datetime.datetime(2012, 1, 5, 0, 0)] 

To rozwiązanie nie wymaga posortowania oryginalnej listy datetime.

1
list_of_dts = [ 
      datetime.datetime(2012,1,1,0,0,0), 
      datetime.datetime(2012,1,1,1,0,0), 
      datetime.datetime(2012,1,2,0,0,0), 
      datetime.datetime(2012,1,3,0,0,0), 
      datetime.datetime(2012,1,5,0,0,0), 
      ] 

groupedByDay={} 
for date in list_of_dts: 
    if date.date() in groupedByDay: 
     groupedByDay[date.date()].append(date) 
    else: 
     groupedByDay[date.date()]=[date] 

Teraz masz słownik, gdzie data jest kluczem, a wartość jest listą podobnych dat.

a jeśli są ustawione na konieczności listę zamiast

result = groupedByDay.values() 
result.sort() 

teraz Wynika to lista list, w którym wszystkie daty z tego samego dnia są zgrupowane razem

+2

'date.date() w groupedByDay.keys()' jest O (n). Czy możesz zastąpić go 'date.date() w groupedByDay'? to O (1). – reclosedev

+0

zrobione. Dzięki za wskazówkę. Niedawno wziąłem Pythona .. ucz się czegoś nowego każdego dnia. – Lex