在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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 15:41:37  来源:igfitidea点击:

Generate list of months between interval in python

pythonpython-2.7

提问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 datetimetype overloads the subtraction operator, so that end - startmakes sense. The result is a timedeltaobject that represents the difference between the two dates, and the .daysattribute gets this difference expressed in days. There is no .monthsattribute, 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 OrderedDictremoves 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.dateobjects 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 rangeto 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()

List of Offset Aliases

偏移别名列表

回答by Anatoly Scherbakov

I came to a solution that uses python-dateutiland works with Python 3.8+:

我找到了一个使用python-dateutilPython 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,
        )
    )