Iteracja poprzez zakres dat w Pythonie
Mam następujący kod, aby to zrobić, ale jak Mogę zrobić to lepiej? W tej chwili myślę, że jest to lepsze niż zagnieżdżone pętle, ale zaczyna być Perl-one-linerish, gdy masz generator w zrozumieniu listy.
day_count = (end_date - start_date).days + 1
for single_date in [d for d in (start_date + timedelta(n) for n in range(day_count)) if d <= end_date]:
print strftime("%Y-%m-%d", single_date.timetuple())
Uwagi
- Nie używam tego do drukowania. To tylko w celach demonstracyjnych.
- zmienne
start_date
iend_date
są obiektamidatetime.date
, ponieważ nie potrzebuję znaczników czasu. (Zostaną wykorzystane do wygenerowania raport).
Przykładowe Wyjście
Dla daty rozpoczęcia 2009-05-30
i daty zakończenia 2009-06-09
:
2009-05-30
2009-05-31
2009-06-01
2009-06-02
2009-06-03
2009-06-04
2009-06-05
2009-06-06
2009-06-07
2009-06-08
2009-06-09
19 answers
Dlaczego są dwie zagnieżdżone iteracje? Dla mnie tworzy tę samą listę danych z tylko jedną iteracją:
for single_date in (start_date + timedelta(n) for n in range(day_count)):
print ...
I Żadna lista nie jest przechowywana, tylko jeden generator jest iterowany. Również "jeśli" w generatorze wydaje się być niepotrzebne.
W końcu Sekwencja liniowa powinna wymagać tylko jednego iteratora, a nie dwóch.
Aktualizacja po dyskusji z Johnem Machinem:
Być może najbardziej eleganckim rozwiązaniem jest użycie funkcji generatora do całkowitego ukrycia / abstrakcji iteracji poza zakresem dat:
from datetime import timedelta, date
def daterange(start_date, end_date):
for n in range(int ((end_date - start_date).days)):
yield start_date + timedelta(n)
start_date = date(2013, 1, 1)
end_date = date(2015, 6, 2)
for single_date in daterange(start_date, end_date):
print single_date.strftime("%Y-%m-%d")
Uwaga: dla spójności z wbudowaną funkcją range()
iteracja ta zatrzymuje przed osiągnięciem end_date
. Więc dla iteracji inkluzywnej użyj następnego dnia, tak jak z range()
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-03 10:35:18
To może być bardziej jasne:
d = start_date
delta = datetime.timedelta(days=1)
while d <= end_date:
print d.strftime("%Y-%m-%d")
d += delta
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-23 21:12:50
Użyj dateutil
biblioteka:
from datetime import date
from dateutil.rrule import rrule, DAILY
a = date(2009, 5, 30)
b = date(2009, 6, 9)
for dt in rrule(DAILY, dtstart=a, until=b):
print dt.strftime("%Y-%m-%d")
Ta biblioteka Pythona ma wiele bardziej zaawansowanych funkcji, niektóre bardzo przydatne, jak relative delta
s - i jest zaimplementowana jako pojedynczy plik (moduł), który można łatwo włączyć do projektu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-10 18:15:36
Pandas jest świetny dla szeregów czasowych w ogóle i ma bezpośrednie wsparcie dla zakresów dat.
import pandas as pd
daterange = pd.date_range(start_date, end_date)
Możesz następnie zapętlić daterange, aby wydrukować datę:
for single_date in daterange:
print (single_date.strftime("%Y-%m-%d"))
Ma również wiele opcji ułatwiających życie. Na przykład, jeśli chcesz tylko dni tygodnia, po prostu zamienisz w bdate_range. Zobacz też http://pandas.pydata.org/pandas-docs/stable/timeseries.html#generating-ranges-of-timestamps
[[3]}moc pand jest tak naprawdę jej ramami danych, które wspierają wektoryzowane operacje (podobnie jak numpy), które sprawiają, że operacje na dużych ilościach danych są bardzo szybkie i łatwe.
Edytuj: Można również całkowicie pominąć pętlę for i po prostu wydrukować ją bezpośrednio, co jest łatwiejsze i bardziej wydajne: {]}
print(daterange)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-23 07:17:20
import datetime
def daterange(start, stop, step=datetime.timedelta(days=1), inclusive=False):
# inclusive=False to behave like range by default
if step.days > 0:
while start < stop:
yield start
start = start + step
# not +=! don't modify object passed in if it's mutable
# since this function is not restricted to
# only types from datetime module
elif step.days < 0:
while start > stop:
yield start
start = start + step
if inclusive and start == stop:
yield start
# ...
for date in daterange(start_date, end_date, inclusive=True):
print strftime("%Y-%m-%d", date.timetuple())
Ta funkcja robi więcej niż wymaga, wspierając ujemny krok, itp. Dopóki uwzględniasz logikę zakresu, nie potrzebujesz oddzielnego day_count
, a co najważniejsze, kod staje się łatwiejszy do odczytania, gdy wywołujesz funkcję z wielu miejsc.
Dlaczego nie spróbować:
import datetime as dt
start_date = dt.datetime(2012, 12,1)
end_date = dt.datetime(2012, 12,5)
total_days = (end_date - start_date).days + 1 #inclusive 5 days
for day_number in range(total_days):
current_date = (start_date + dt.timedelta(days = day_number)).date()
print current_date
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-23 13:35:24
To najbardziej czytelne dla człowieka rozwiązanie, jakie przychodzi mi do głowy.
import datetime
def daterange(start, end, step=datetime.timedelta(1)):
curr = start
while curr < end:
yield curr
curr += step
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-13 14:28:08
Funkcja Numpy ' ego arange
może być zastosowana do dat:
import numpy as np
from datetime import datetime, timedelta
d0 = datetime(2009, 1,1)
d1 = datetime(2010, 1,1)
dt = timedelta(days = 1)
dates = np.arange(d0, d1, dt).astype(datetime)
Użycie {[2] } polega na przekonwertowaniu z numpy.datetime64
do tablicy datetime.datetime
obiektów.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-29 10:53:27
Pokaż ostatnie n dni od dzisiaj:
import datetime
for i in range(0, 100):
print((datetime.date.today() + datetime.timedelta(i)).isoformat())
Wyjście:
2016-06-29
2016-06-30
2016-07-01
2016-07-02
2016-07-03
2016-07-04
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-21 23:27:07
import datetime
def daterange(start, stop, step_days=1):
current = start
step = datetime.timedelta(step_days)
if step_days > 0:
while current < stop:
yield current
current += step
elif step_days < 0:
while current > stop:
yield current
current += step
else:
raise ValueError("daterange() step_days argument must not be zero")
if __name__ == "__main__":
from pprint import pprint as pp
lo = datetime.date(2008, 12, 27)
hi = datetime.date(2009, 1, 5)
pp(list(daterange(lo, hi)))
pp(list(daterange(hi, lo, -1)))
pp(list(daterange(lo, hi, 7)))
pp(list(daterange(hi, lo, -7)))
assert not list(daterange(lo, hi, -1))
assert not list(daterange(hi, lo))
assert not list(daterange(lo, hi, -7))
assert not list(daterange(hi, lo, 7))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-30 11:49:11
Mam podobny problem, ale muszę powtarzać co miesiąc zamiast codziennie.
This is my solution
import calendar
from datetime import datetime, timedelta
def days_in_month(dt):
return calendar.monthrange(dt.year, dt.month)[1]
def monthly_range(dt_start, dt_end):
forward = dt_end >= dt_start
finish = False
dt = dt_start
while not finish:
yield dt.date()
if forward:
days = days_in_month(dt)
dt = dt + timedelta(days=days)
finish = dt > dt_end
else:
_tmp_dt = dt.replace(day=1) - timedelta(days=1)
dt = (_tmp_dt.replace(day=dt.day))
finish = dt < dt_end
Przykład #1
date_start = datetime(2016, 6, 1)
date_end = datetime(2017, 1, 1)
for p in monthly_range(date_start, date_end):
print(p)
Wyjście
2016-06-01
2016-07-01
2016-08-01
2016-09-01
2016-10-01
2016-11-01
2016-12-01
2017-01-01
Przykład #2
date_start = datetime(2017, 1, 1)
date_end = datetime(2016, 6, 1)
for p in monthly_range(date_start, date_end):
print(p)
Wyjście
2017-01-01
2016-12-01
2016-11-01
2016-10-01
2016-09-01
2016-08-01
2016-07-01
2016-06-01
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-24 07:34:17
for i in range(16):
print datetime.date.today() + datetime.timedelta(days=i)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-10 18:04:10
Nie mogę uwierzyć* to pytanie istnieje od 9 lat bez nikogo sugerującego prostą funkcję rekurencyjną:
from datetime import datetime, timedelta
def walk_days(start_date, end_date):
if start_date <= end_date:
print(start_date.strftime("%Y-%m-%d"))
next_date = start_date + timedelta(days=1)
walk_days(next_date, end_date)
#demo
start_date = datetime(2009, 5, 30)
end_date = datetime(2009, 6, 9)
walk_days(start_date, end_date)
Wyjście:
2009-05-30
2009-05-31
2009-06-01
2009-06-02
2009-06-03
2009-06-04
2009-06-05
2009-06-06
2009-06-07
2009-06-08
2009-06-09
Edit: *Teraz mogę w to uwierzyć -- zobacz czy Python optymalizuje rekurencję ogonową? . Dziękuję Tim .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-31 12:29:27
Możesz wygenerować serię dat pomiędzy dwoma datami używając biblioteki pandas w prosty i zaufany sposób
import pandas as pd
print pd.date_range(start='1/1/2010', end='1/08/2018', freq='M')
Możesz zmienić częstotliwość generowania dat, ustawiając freq jako D, M, Q, Y (dzienne, miesięczne, kwartalne, roczne )
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-09 05:41:50
A co z następującym zakresem powiększonym o dni:
for d in map( lambda x: startDate+datetime.timedelta(days=x), xrange( (stopDate-startDate).days ) ):
# Do stuff here
- startDate i stopDate to datetime.obiekty daty
Dla wersji ogólnej:
for d in map( lambda x: startTime+x*stepTime, xrange( (stopTime-startTime).total_seconds() / stepTime.total_seconds() ) ):
# Do stuff here
- startTime i stopTime to datetime.Data lub datetime.obiekt datetime (oba powinny być tego samego typu)
- stepTime jest obiektem timedelta
Zauważ to .total_seconds() jest obsługiwane tylko po Pythonie 2.7, jeśli utkniesz z wcześniejszą wersją, Możesz napisać własną Funkcja:
def total_seconds( td ):
return float(td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-13 03:42:20
Ta funkcja ma kilka dodatkowych funkcji:
- może przekazać łańcuch pasujący do DATE_FORMAT dla początku lub końca i jest konwertowany do obiektu date
- może przekazać obiekt date dla początku lub końca
-
Sprawdzanie błędów w przypadku, gdy koniec jest starszy niż początek
import datetime from datetime import timedelta DATE_FORMAT = '%Y/%m/%d' def daterange(start, end): def convert(date): try: date = datetime.datetime.strptime(date, DATE_FORMAT) return date.date() except TypeError: return date def get_date(n): return datetime.datetime.strftime(convert(start) + timedelta(days=n), DATE_FORMAT) days = (convert(end) - convert(start)).days if days <= 0: raise ValueError('The start date must be before the end date.') for n in range(0, days): yield get_date(n) start = '2014/12/1' end = '2014/12/31' print list(daterange(start, end)) start_ = datetime.date.today() end = '2015/12/1' print list(daterange(start, end))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-27 04:01:52
Oto kod dla funkcji ogólnego zakresu dat, podobny do odpowiedzi Bera, ale bardziej elastyczny:
def count_timedelta(delta, step, seconds_in_interval):
"""Helper function for iterate. Finds the number of intervals in the timedelta."""
return int(delta.total_seconds() / (seconds_in_interval * step))
def range_dt(start, end, step=1, interval='day'):
"""Iterate over datetimes or dates, similar to builtin range."""
intervals = functools.partial(count_timedelta, (end - start), step)
if interval == 'week':
for i in range(intervals(3600 * 24 * 7)):
yield start + datetime.timedelta(weeks=i) * step
elif interval == 'day':
for i in range(intervals(3600 * 24)):
yield start + datetime.timedelta(days=i) * step
elif interval == 'hour':
for i in range(intervals(3600)):
yield start + datetime.timedelta(hours=i) * step
elif interval == 'minute':
for i in range(intervals(60)):
yield start + datetime.timedelta(minutes=i) * step
elif interval == 'second':
for i in range(intervals(1)):
yield start + datetime.timedelta(seconds=i) * step
elif interval == 'millisecond':
for i in range(intervals(1 / 1000)):
yield start + datetime.timedelta(milliseconds=i) * step
elif interval == 'microsecond':
for i in range(intervals(1e-6)):
yield start + datetime.timedelta(microseconds=i) * step
else:
raise AttributeError("Interval must be 'week', 'day', 'hour' 'second', \
'microsecond' or 'millisecond'.")
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-15 17:59:27
> pip install DateTimeRange
from datetimerange import DateTimeRange
def dateRange(start, end, step):
rangeList = []
time_range = DateTimeRange(start, end)
for value in time_range.range(datetime.timedelta(days=step)):
rangeList.append(value.strftime('%m/%d/%Y'))
return rangeList
dateRange("2018-09-07", "2018-12-25", 7)
Out[92]:
['09/07/2018',
'09/14/2018',
'09/21/2018',
'09/28/2018',
'10/05/2018',
'10/12/2018',
'10/19/2018',
'10/26/2018',
'11/02/2018',
'11/09/2018',
'11/16/2018',
'11/23/2018',
'11/30/2018',
'12/07/2018',
'12/14/2018',
'12/21/2018']
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-11 16:04:44
Nieco inne podejście do odwracalnych kroków poprzez przechowywanie range
args w krotce.
def date_range(start, stop, step=1, inclusive=False):
day_count = (stop - start).days
if inclusive:
day_count += 1
if step > 0:
range_args = (0, day_count, step)
elif step < 0:
range_args = (day_count - 1, -1, step)
else:
raise ValueError("date_range(): step arg must be non-zero")
for i in range(*range_args):
yield start + timedelta(days=i)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-03 16:13:41