2015-12-20 9 views
6

Mi piacerebbe trovare tutti i martedì e mercoledì (come datetime oggetto) tra 2015-11-02 e 2015-12-14. Questo funziona:Tutti i martedì e mercoledì in un intervallo di date: c'è un modo più pitonico?

from datetime import datetime, timedelta 

l = [] 
for i in range(100): 
    d = datetime(2015,11,2) + timedelta(days=i) 
    if d > datetime(2015,12,14): 
     break 
    if d.weekday() == 1 or d.weekday() == 2: # tuesday or wednesday 
     l.append(d) 
print l 

[datetime.datetime (2015, 11, 3, 0, 0), datetime.datetime (2015, 11, 4, 0, 0), datetime.datetime (2015, 11 , 10, 0, 0), datetime.datetime (2015, 11, 11, 0, 0), datetime.datetime (2015, 11, 17, 0, 0), datetime.datetime (2015, 11, 18, 0, 0), datetime.datetime (2015, 11, 24, 0, 0), datetime.datetime (2015, 11, 25, 0, 0), datetime.datetime (2015, 12, 1, 0, 0), datetime. datetime (2015, 12, 2, 0, 0), datetime.datetime (2015, 12, 8, 0, 0), datetime.datetime (2015, 12, 9, 0, 0)]

C'è un modo più pititico per farlo?

risposta

2

Come ho già uso pandas, questo funziona:

import pandas as pd 
print [d for d in pd.date_range(start="2015-11-02", end="2015-12-14") if d.weekday() in [1,2]] 
6
from datetime import datetime, timedelta 

start, end = datetime(2015, 11, 2), datetime(2015, 12, 14) 
days = (start + timedelta(days=i) for i in range((end - start).days + 1)) 
l = [d for d in days if d.weekday() in [1,2] ] 

Questo sarà molto più veloce se si sta andando per un lungo arco:

def helper(d, i, inc): 
    while d.weekday() != i: 
     d += timedelta(days=inc) 
    return d.replace(hour=0, minute=0, second=0, microsecond=0) 


start, end = datetime(2015, 11, 02), datetime(2015,12, 14) 


def find_days(st, end, d1, d2): 
    if st >= end: 
     raise ValueError("Start must be before end") 
    else: 
     _st, _end = helper(st, d1, inc=-1), helper(end, d2, 1) 
     secs = (_end - _st).total_seconds() // 86400 
     if st.weekday() == d2: 
      yield st 
     for i in range(int(secs/7) + 1): 
      if st <= _st <= end: 
       yield _st 
       nxt = _st + timedelta(days=1) 
       if nxt <= end: 
        yield nxt 
      _st += timedelta(days=7) 
    if _st <= end: 
     yield _st 
    from pprint import pprint as pp 



from pprint import pprint as pp 


pp(list(find_days(start, end, 1, 2))) 

uscita:

[datetime.datetime(2015, 11, 3, 0, 0), 
datetime.datetime(2015, 11, 4, 0, 0), 
datetime.datetime(2015, 11, 10, 0, 0), 
datetime.datetime(2015, 11, 11, 0, 0), 
datetime.datetime(2015, 11, 17, 0, 0), 
datetime.datetime(2015, 11, 18, 0, 0), 
datetime.datetime(2015, 11, 24, 0, 0), 
datetime.datetime(2015, 11, 25, 0, 0), 
datetime.datetime(2015, 12, 1, 0, 0), 
datetime.datetime(2015, 12, 2, 0, 0), 
datetime.datetime(2015, 12, 8, 0, 0), 
datetime.datetime(2015, 12, 9, 0, 0)] 

Questo fa quello dateutil funziona e cosa più veloce:

In [12]: def dte(): 
....:   results = rrule(DAILY, 
....:     dtstart = dt.datetime(2015,11, 2), 
....:     until = end, 
....:     byweekday=(TU, WE), 
....:  ) 
....:   return list(results) 
....: 


In [38]: start, end = datetime(2015, 11, 2), datetime(2100, 11, 14) 

In [39]: for i in range(600): 
      end += timedelta(days=1) 
      assert dte() == list(find_days(start, end,1,2)) 
    ....:  

In [40]: start, end = datetime(2015, 11, 2), datetime(2017, 11, 14) 


In [41]: timeit [d for d in date_range(start, end) if d.weekday() in (1, 2)] 
10 loops, best of 3: 62.1 ms per loop 

In [42]: timeit list(find_days(start, end, 1, 2)) 
100 loops, best of 3: 8.11 ms per loop 

In [43]: timeit dte() 
10 loops, best of 3: 131 ms per loop 
+1

'se st> end o st == FINE' - Si potrebbe ottimizzare questo. – 7stud

+0

@ 7stud, sì, non sono sicuro del motivo per cui l'ho scritto in quel modo –

3

Qui è con il terzo modulo parte python-dateutils:

from dateutil.rrule import rrule, DAILY, TU, WE 
import datetime as dt 

results = rrule(DAILY, 
     dtstart = dt.datetime(2015,11,2), 
     until = dt.datetime(2015, 12, 14), 
     byweekday = (TU, WE), 
) 


for result in results: 
    print(result) 

--output:-- 
2015-11-03 00:00:00 
2015-11-04 00:00:00 
2015-11-10 00:00:00 
2015-11-11 00:00:00 
2015-11-17 00:00:00 
2015-11-18 00:00:00 
2015-11-24 00:00:00 
2015-11-25 00:00:00 
2015-12-01 00:00:00 
2015-12-02 00:00:00 
2015-12-08 00:00:00 
2015-12-09 00:00:00 
4

Se si dispone di un semplice intervallo di dati (simile alla funzione di Python range):

import datetime as dt 

def date_range(d1, d2): 
    d=d1 
    while d<=d2: 
     yield d 
     d+=dt.timedelta(days=1) 

quindi è possibile utilizzare un semplice elenco di comprensione:

>>> '\n'.join([d.isoformat() for d in date_range(dt.date(2015,11,2),dt.date(2015,12,14)) if d.weekday() in (1,2)]) 
2015-11-03 
2015-11-04 
2015-11-10 
2015-11-11 
2015-11-17 
2015-11-18 
2015-11-24 
2015-11-25 
2015-12-01 
2015-12-02 
2015-12-08 
2015-12-09 

Se si teme che si tratta di uno spreco giorno iterazione per giorno, momento in cui:

$ python -m timeit ' 
> import datetime as dt 
> def date_range(d1, d2, step=1): 
>  d=d1 
>  while True: 
>   if d+dt.timedelta(days=step)>d2: 
>    break 
>   yield d 
>   d+=dt.timedelta(days=step) 
> [d.isoformat() for d in date_range(dt.date(1815,11,2),dt.date(2215,12,14)) if d.weekday() in (1,2)] 
> ' 
10 loops, best of 3: 214 msec per loop 

Calcolo di 400 anni di martedì e mercoledì richiede 1/4 di secondo e circa 100 millisec per un intervallo di 1 anno. Saluti.

Problemi correlati