Создайте список месяцев между интервалом в python
Я хочу создать список python, содержащий все месяцы, происходящие между двумя датами, при этом вход и выход будут отформатированы следующим образом:
date1 = "2014-10-10" # input start date
date2 = "2016-01-07" # input end date
month_list = ['Oct-14', 'Nov-14', 'Dec-14', 'Jan-15', 'Feb-15', 'Mar-15', 'Apr-15', 'May-15', 'Jun-15', 'Jul-15', 'Aug-15', 'Sep-15', 'Oct-15', 'Nov-15', 'Dec-15', 'Jan-16'] # output
Ответы
Ответ 1
>>> from datetime import datetime, timedelta
>>> from collections import OrderedDict
>>> dates = ["2014-10-10", "2016-01-07"]
>>> start, end = [datetime.strptime(_, "%Y-%m-%d") for _ in dates]
>>> OrderedDict(((start + timedelta(_)).strftime(r"%b-%y"), None) for _ in xrange((end - start).days)).keys()
['Oct-14', 'Nov-14', 'Dec-14', 'Jan-15', 'Feb-15', 'Mar-15', 'Apr-15', 'May-15', 'Jun-15', 'Jul-15', 'Aug-15', 'Sep-15', 'Oct-15', 'Nov-15', 'Dec-15', 'Jan-16']
Обновление: немного объяснений, как было запрошено в одном комментарии. Здесь три проблемы: анализ дат в соответствующих структурах данных (strptime
); получение диапазона дат с учетом двух крайностей и шага (один месяц); форматирование выходных дат (strftime
). Тип datetime
перегружает оператор вычитания, поэтому end - start
имеет смысл. Результатом является объект timedelta
который представляет разницу между двумя датами, а атрибут .days
получает эту разницу, выраженную в днях. Не существует атрибута .months
, поэтому мы повторяем один раз за один раз и преобразуем даты в желаемый формат вывода. Это дает много дубликатов, которые OrderedDict
удаляет, сохраняя элементы в правильном порядке.
Теперь это просто и красно, потому что позволяет модулю datetime выполнять всю работу, но это также ужасно неэффективно. Мы призываем много методов для каждого дня, пока нам нужно только выводить месяцы. Если производительность не является проблемой, приведенный выше код будет в порядке. В противном случае нам придется работать немного больше. Давайте сравним приведенную выше реализацию с более эффективной:
from datetime import datetime, timedelta
from collections import OrderedDict
dates = ["2014-10-10", "2016-01-07"]
def monthlist_short(dates):
start, end = [datetime.strptime(_, "%Y-%m-%d") for _ in dates]
return OrderedDict(((start + timedelta(_)).strftime(r"%b-%y"), None) for _ in xrange((end - start).days)).keys()
def monthlist_fast(dates):
start, end = [datetime.strptime(_, "%Y-%m-%d") for _ in dates]
total_months = lambda dt: dt.month + 12 * dt.year
mlist = []
for tot_m in xrange(total_months(start)-1, total_months(end)):
y, m = divmod(tot_m, 12)
mlist.append(datetime(y, m+1, 1).strftime("%b-%y"))
return mlist
assert monthlist_fast(dates) == monthlist_short(dates)
if __name__ == "__main__":
from timeit import Timer
for func in "monthlist_short", "monthlist_fast":
print func, Timer("%s(dates)" % func, "from __main__ import dates, %s" % func).timeit(1000)
На моем ноутбуке я получаю следующий вывод:
monthlist_short 2.3209939003
monthlist_fast 0.0774540901184
Конкретная реализация примерно в 30 раз медленнее, поэтому я бы не рекомендовал ее в критически важных приложениях :)
Ответ 2
Я нашел очень лаконичный способ сделать это с Пандами, поделившись информацией на случай, если это кому-нибудь поможет:
ОБНОВЛЕНИЕ: Я получил его в одну строку с помощью этого сообщения :)
pd.date_range('2014-10-10','2016-01-07',
freq='MS').strftime("%Y-%b").tolist()
СТАРЫЙ ОТВЕТ:
daterange = pd.date_range('2014-10-10','2016-01-07' , freq='1M')
daterange = daterange.union([daterange[-1] + 1])
daterange = [d.strftime('%y-%b') for d in daterange]
Вторая строка предотвращает удаление последней даты из списка.
Ответ 3
Вы должны использовать Календарь и Datetime
import calendar
from datetime import *
date1 = datetime.strptime("2014-10-10", "%Y-%m-%d")
date2 = datetime.strptime("2016-01-07", "%Y-%m-%d")
date1 = date1.replace(day = 1)
date2 = date2.replace(day = 1)
months_str = calendar.month_name
months = []
while date1 < date2:
month = date1.month
year = date1.year
month_str = months_str[month][0:3]
months.append("{0}-{1}".format(month_str,str(year)[-2:]))
next_month = month+1 if month != 12 else 1
next_year = year + 1 if next_month == 1 else year
date1 = date1.replace( month = next_month, year= next_year)
print months
Этот код возвращает
['Oct-14', 'Nov-14', 'Dec-14', 'Jan-14', 'Feb-15', 'Mar-15', 'Apr-15', 'May-15', 'Jun-15', 'Jul-15', 'Aug-15', 'Sep-15', 'Oct-15', 'Nov-15', 'Dec-15', 'Jan-15']
Ответ 4
С пандами у вас может быть один лайнер:
import pandas as pd
date1 = "2014-10-10" # input start date
date2 = "2016-01-07" # input end date
month_list = [i.strftime("%b-%y") for i in pd.date_range(start=date1, end=date2, freq='MS')]
Ответ 5
Проделав подобный материал ранее, я принял удар по решению этого. Использование отдельных компонентов для этого является более гибким и позволяет вам смешивать и сопоставлять их для разных случаев использования. Они также могут быть легче протестированы таким образом, как вы можете видеть в доктринах в iterate_months
.
Также я предлагаю использовать объекты datetime.date
для ввода, поскольку вы можете просто сделать больше с ними. Для этого вам придется сначала проанализировать введенную строку, но это очень легко сделать.
Разбор строк даты
def datify(date):
if isinstance(date, datetime.date):
return date
elif isinstance(date, datetime.datetime):
return date.date()
else:
# taken from simleo answer
return datetime.strptime(date, "%Y-%m-%d")
Во-первых, мы повторяем месяцы
import datetime
def iterate_months(start_date, end_date):
"""Iterate monthly between two given dates.
Emitted will be the first day of each month.
>>> list(iterate_months(datetime.date(1999, 11, 1),
... datetime.date(2000, 2, 1)))
[datetime.date(1999, 11, 1), datetime.date(1999, 12, 1),\
datetime.date(2000, 1, 1), datetime.date(2000, 2, 1)]
"""
assert isinstance(start_date, datetime.date)
assert isinstance(end_date, datetime.date)
assert start_date < end_date
year = start_date.year
month = start_date.month
while True:
current = datetime.date(year, month, 1)
yield current
if current.month == end_date.month and current.year == end_date.year:
break
else:
month = ((month + 1) % 12) or 12
if month == 1:
year += 1
if __name__ == '__main__':
import doctest
doctest.testmod()
Чтобы отформатировать даты, используйте что-то вроде этого
def format_month(date):
return date.strftime(r"%b-%y")
Объединяя все это
start = datify("2014-10-10")
end = datify("2016-01-07")
for entry in iterate_months(start, end):
print format_month(entry)
Или сохраните его как список:
result = list(iterate_months(start, end))
Ответ 6
Найдите ниже мой подход к этой проблеме, используя раздельные и простые итерации на основе модуляции без импорта какого-либо специального модуля.
date1 = "2014-10-10"
date2 = "2016-01-07"
y0 = int( date1.split('-')[0] ) # 2014
y1 = int( date2.split('-')[0] ) # 2016
m0 = int( date1.split('-')[1] ) - 1 # 10-1 --> 9 because will be used for indexing
m1 = int( date2.split('-')[1] ) - 1 # 01-1 --> 0 because will be used for indexing
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
result = []
start = m0
for y in range(y0, y1+1):
for m in range(start,12):
result.append( str( months[m % 12])+'-'+str(y) )
if y == y1 and (m % 12) == m1:
break
start = 0
print result
$ python date.py
['Oct-2014', 'Nov-2014', 'Dec-2014', 'Jan-2015', 'Feb-2015', 'Mar-2015', 'Apr-2015', 'May-2015', 'Jun-2015', 'Jul-2015', 'Aug-2015', 'Sep-2015', 'Oct-2015', 'Nov-2015', 'Dec-2015', 'Jan-2016']
Ответ 7
Если вы хотите сохранить ваши даты в формате Python, вы можете попробовать использовать to_pydatetime()
.
import pandas as pd
from datetime import datetime
datemin = datetime(2010, 1, 1)
datemax = datetime(2019, 12, 31)
# First day of month
pd.date_range(datemin, datemax, freq='MS').to_pydatetime().tolist()
# Last day of month
pd.date_range(datemin, datemax, freq='M').to_pydatetime().tolist()
Список смещенных псевдонимов
Ответ 8
Вот мое решение с простым пониманием списка, которое использует range
чтобы знать, где должны начинаться и заканчиваться месяцы
from datetime import datetime as dt
sd = dt.strptime('2014-10-10', "%Y-%m-%d")
ed = dt.strptime('2016-01-07', "%Y-%m-%d")
lst = [dt.strptime('%2.2d-%2.2d' % (y, m), '%Y-%m').strftime('%b-%y') \
for y in xrange(sd.year, ed.year+1) \
for m in xrange(sd.month if y==sd.year else 1, ed.month+1 if y == ed.year else 13)]
print lst
производит
['Oct-14', 'Nov-14', 'Dec-14', 'Jan-15', 'Feb-15', 'Mar-15', 'Apr-15', 'May-15', 'Jun-15', 'Jul-15', 'Aug-15', 'Sep-15', 'Oct-15', 'Nov-15', 'Dec-15', 'Jan-16']