在python中生成间隔之间的月份列表
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34898525/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Generate list of months between interval in python
提问by Birlla
I want to generate a python list containing all months occurring between two dates, with the input and output formatted as follows:
我想生成一个包含两个日期之间发生的所有月份的 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
采纳答案by simleo
>>> 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']
Update:a bit of explanation, as requested in one comment. There are three problems here: parsing the dates into appropriate data structures (strptime
); getting the date range given the two extremes and the step (one month); formatting the output dates (strftime
). The datetime
type overloads the subtraction operator, so that end - start
makes sense. The result is a timedelta
object that represents the difference between the two dates, and the .days
attribute gets this difference expressed in days. There is no .months
attribute, so we iterate one day at a time and convert the dates to the desired output format. This yields a lot of duplicates, which the OrderedDict
removes while keeping the items in the right order.
更新:根据一条评论的要求进行一些解释。这里存在三个问题:将日期解析为适当的数据结构(strptime
);获取给定两个极端和步骤(一个月)的日期范围;格式化输出日期 ( strftime
)。该datetime
类型重载了减法运算符,所以这end - start
是有道理的。结果是一个timedelta
表示两个日期之间差异的对象,该.days
属性获取以天表示的这种差异。没有.months
属性,因此我们一次迭代一天并将日期转换为所需的输出格式。这会产生大量重复项,在OrderedDict
将项目保持在正确顺序的同时将其删除。
Now this is simple and concise because it lets the datetime module do all the work, but it's also horribly inefficient. We're calling a lot of methods for each day while we only need to output months. If performance is not an issue, the above code will be just fine. Otherwise, we'll have to work a bit more. Let's compare the above implementation with a more efficient one:
现在这很简单,因为它让 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)
On my laptop, I get the following output:
在我的笔记本电脑上,我得到以下输出:
monthlist_short 2.3209939003
monthlist_fast 0.0774540901184
The concise implementation is about 30 times slower, so I would not recommend it in time-critical applications :)
简洁的实现大约慢 30 倍,所以我不会在时间关键的应用程序中推荐它:)
回答by Luis González
You have to use Calendarand 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
This code returns
此代码返回
['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']
回答by pi.
Having done similar stuff previously, I took a stab at solving this. Using distinct components for doing this is more flexible and enables you to mix and match them for different use-cases. They also can be tested more easily this way, as you can see by the doctests in iterate_months
.
之前做过类似的事情,我尝试解决这个问题。使用不同的组件来执行此操作更加灵活,并使您能够针对不同的用例混合和匹配它们。也可以通过这种方式更轻松地测试它们,正如您在iterate_months
.
Also I suggest to use datetime.date
objects for your input as you can just do more with those. To do that you'll have to first parse your input string, but this is very easily done.
此外,我建议使用datetime.date
对象作为输入,因为您可以用这些对象做更多的事情。为此,您必须首先解析您的输入字符串,但这很容易完成。
Parsing the date-strings
解析日期字符串
def datify(date):
if isinstance(date, datetime.date):
return date
elif isinstance(date, datetime.datetime):
return date.date()
else:
# taken from simleo's answer
return datetime.strptime(date, "%Y-%m-%d")
First, we iterate through the months
首先,我们遍历几个月
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()
To format your dates, use something like this
要格式化您的日期,请使用以下内容
def format_month(date):
return date.strftime(r"%b-%y")
Putting it all together
把这一切放在一起
start = datify("2014-10-10")
end = datify("2016-01-07")
for entry in iterate_months(start, end):
print format_month(entry)
Or save it as a list:
或将其另存为列表:
result = list(iterate_months(start, end))
回答by Pynchia
Here is my solution with a simple list comprehension which uses range
to know where months must start and end
这是我的解决方案,带有一个简单的列表理解,用于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
produces
产生
['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']
回答by funk
Find below my approach to this problem using splitand simple modulo-basediterations without importing any special module.
在下面找到我使用拆分和简单的基于模的迭代解决此问题的方法,而无需导入任何特殊模块。
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 dates.py
$ python 日期.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']
回答by atkat12
I found a very succinct way to do this with Pandas, sharing in case it helps anybody:
我找到了一个非常简洁的方法来用 Pandas 来做这件事,分享一下,以防它对任何人有帮助:
UPDATE:I've got it down to a one-liner with the help of this post:)
更新:在这篇文章的帮助下,我已经把它归结为单行:)
pd.date_range('2014-10-10','2016-01-07',
freq='MS').strftime("%Y-%b").tolist()
OLD ANSWER:
旧答案:
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]
The second line prevents the last date from getting clipped off the list.
第二行防止最后日期从列表中删除。
回答by tmsss
With pandas, you can have a one liner like this:
使用熊猫,你可以有一个这样的单线:
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')]
回答by Nunya
If you're interested in keeping your dates in a Python format, you can try using to_pydatetime()
.
如果您有兴趣以 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()
回答by Anatoly Scherbakov
I came to a solution that uses python-dateutil
and works with Python 3.8+:
我找到了一个使用python-dateutil
Python 3.8+的解决方案:
https://gist.github.com/anatoly-scherbakov/593770d446a06f109438a134863ba969
https://gist.github.com/anatoly-scherbakov/593770d446a06f109438a134863ba969
def month_range(
start: datetime.date,
end: datetime.date,
) -> Iterator[datetime.date]:
"""Yields the 1st day of each month in the given date range."""
yield from itertools.takewhile(
lambda date: date < end,
itertools.accumulate(
itertools.repeat(relativedelta(months=1)),
operator.add,
initial=start,
)
)