2015-04-18 11 views
9

Sto accedendo ad alcuni dati tramite un'API in cui devo fornire l'intervallo di date per la mia richiesta, es. start = '20100101', fine = '20150415'. Ho pensato che avrei accelerato suddividendo l'intervallo di date in intervalli non sovrapposti e utilizzando il multiprocessing su ciascun intervallo.Dato un intervallo di date, come possiamo suddividerlo in N sotto-intervalli contigui?

Il mio problema è che il modo in cui sto suddividendo l'intervallo di date non mi dà costantemente il risultato previsto. Ecco cosa ho fatto:

from datetime import date 

begin = '20100101' 
end = '201' 

Supponiamo di voler suddividere questo in quarti. Primo cambio la stringa in date:

def get_yyyy_mm_dd(yyyymmdd): 
    # given string 'yyyymmdd' return (yyyy, mm, dd) 
    year = yyyymmdd[0:4] 
    month = yyyymmdd[4:6] 
    day = yyyymmdd[6:] 
    return int(year), int(month), int(day) 

y1, m1, d1 = get_yyyy_mm_dd(begin) 
d1 = date(y1, m1, d1) 
y2, m2, d2 = get_yyyy_mm_dd(end) 
d2 = date(y2, m2, d2) 

poi dividere questa gamma in sotto-intervalli:

def remove_tack(dates_list): 
    # given a list of dates in form YYYY-MM-DD return a list of strings in form 'YYYYMMDD' 
    tackless = [] 
    for d in dates_list: 
     s = str(d) 
     tackless.append(s[0:4]+s[5:7]+s[8:]) 
    return tackless 

def divide_date(date1, date2, intervals): 
    dates = [date1] 
    for i in range(0, intervals): 
     dates.append(dates[i] + (date2 - date1)/intervals) 
    return remove_tack(dates) 

Utilizzando inizio e fine dall'alto otteniamo:

listdates = divide_date(d1, d2, 4) 
print listdates # ['20100101', '20100402', '20100702', '20101001', '201'] looks correct 

Ma se invece Uso le date:

begin = '20150101' 
end = '20150228' 

...

listdates = divide_date(d1, d2, 4) 
print listdates # ['20150101', '20150115', '20150129', '20150212', '20150226'] 

Mi mancano due giorni alla fine di febbraio. Non ho bisogno di tempo o fuso orario per la mia applicazione e non mi dispiace installare un'altra libreria.

risposta

12

avrei effettivamente seguire un approccio diverso e si basano su timedelta e quella aggiunta per determinare gli intervalli non sovrapposti

Attuazione

def date_range(start, end, intv): 
    from datetime import datetime 
    start = datetime.strptime(start,"%Y%m%d") 
    end = datetime.strptime(end,"%Y%m%d") 
    diff = (end - start)/intv 
    for i in range(intv): 
     yield (start + diff * i).strftime("%Y%m%d") 
    yield end.strftime("%Y%m%d") 

Esecuzione

>>> begin = '20150101' 
>>> end = '20150228' 
>>> list(date_range(begin, end, 4)) 
['20150101', '20150115', '20150130', '20150213', '20150228'] 
+0

Funziona alla grande! Conciso e senza tutte le mie funzioni intermedie. Grazie! – Scott

+1

Hai appena notato qualcosa che forse potresti voler modificare.Nella tua risposta: yield (d1 + diff * i) .strftime ("% Y% m% d"), d1 è globale e funziona con il mio codice, ma in generale d1 dovrebbe essere avviato. – Scott

+0

@Scott: Grazie. Il mio codice originale era con le variabili d1, d2, ma in seguito l'ho cambiato in qualcosa di significativo in questo post, ma apparentemente mancava un paio di modifiche. – Abhijit

1

Could usi invece gli oggetti datetime.date?

Se lo fai:

import datetime 
begin = datetime.date(2001, 1, 1) 
end = datetime.date(2010, 12, 31) 

intervals = 4 

date_list = [] 

delta = (end - begin)/4 
for i in range(1, intervals + 1): 
    date_list.append((begin+i*delta).strftime('%Y%m%d')) 

e date_list dovrebbe avere le date di fine per ciascun intervallo di manutenzione.

2

si dovrebbe cambiare la data per datetime

from datetime import date, datetime, timedelta 

begin = '20150101' 
end = '20150228' 

def get_yyyy_mm_dd(yyyymmdd): 
    # given string 'yyyymmdd' return (yyyy, mm, dd) 
    year = yyyymmdd[0:4] 
    month = yyyymmdd[4:6] 
    day = yyyymmdd[6:] 
    return int(year), int(month), int(day) 

y1, m1, d1 = get_yyyy_mm_dd(begin) 
d1 = datetime(y1, m1, d1) 
y2, m2, d2 = get_yyyy_mm_dd(end) 
d2 = datetime(y2, m2, d2) 

def remove_tack(dates_list): 
    # given a list of dates in form YYYY-MM-DD return a list of strings in form 'YYYYMMDD' 
    tackless = [] 
    for d in dates_list: 
    s = str(d) 
    tackless.append(s[0:4]+s[5:7]+s[8:]) 
    return tackless 

def divide_date(date1, date2, intervals): 
    dates = [date1] 
    delta = (date2-date1).total_seconds()/4 
    for i in range(0, intervals): 
    dates.append(dates[i] + timedelta(0,delta)) 
    return remove_tack(dates) 

listdates = divide_date(d1, d2, 4) 
print listdates 

risultato:

[ '20.150.101 0:00:00', '20.150.115 00:00:00', '20.150.130 00:00: 00' , '20.150.213 00:00:00', '20.150.228 0:00:00']

+0

Grazie per aver segnalato questo. Ho accettato la risposta di @ Abhijit poiché pulisce un po 'il mio codice. – Scott

+0

@Scott naturalmente, la risposta di Abhijit è migliore, anche io la vota;) –

1

Utilizzando Datetimeindex e Periodi dal Panda, insieme con la comprensione dizionario:

01.235.164,106174 millions
import pandas as pd 

begin = '20100101' 
end = '201' 

start = dt.datetime.strptime(begin, '%Y%m%d') 
finish = dt.datetime.strptime(end, '%Y%m%d') 

dates = pd.DatetimeIndex(start=start, end=finish, freq='D').tolist() 
quarters = [d.to_period('Q') for d in dates] 
df = pd.DataFrame([quarters, dates], index=['Quarter', 'Date']).T 

quarterly_dates = {str(q): [ts.strftime('%Y%m%d') 
          for ts in df[df.Quarter == q].Date.values.tolist()] 
          for q in quarters} 

>>> quarterly_dates 
{'2010Q1': ['20100101', 
    '20100102', 
    '20100103', 
    '20100104', 
    '20100105', 
... 
    '20101227', 
    '20101228', 
    '20101229', 
    '201', 
    '201']} 

>>> quarterly_dates.keys() 
['2010Q1', '2010Q2', '2010Q3', '2010Q4']