迭代Python中的一系列日期

Sha*_*ilo 334 python iteration datetime

我有以下代码来执行此操作,但我怎样才能做得更好?现在我认为它比嵌套循环更好,但是当你在列表理解中有一个生成器时,它开始得到Perl-one-liner.

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())
Run Code Online (Sandbox Code Playgroud)

笔记

  • 我实际上并没有用它来打印.这只是为了演示目的.
  • start_dateend_date变量是datetime.date因为我不需要时间戳对象.(它们将用于生成报告).

样本输出

对于开始日期2009-05-30和结束日期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
Run Code Online (Sandbox Code Playgroud)

Ber*_*Ber 498

为什么有两个嵌套迭代?对我来说,它只产生一次迭代产生相同的数据列表:

for single_date in (start_date + timedelta(n) for n in range(day_count)):
    print ...
Run Code Online (Sandbox Code Playgroud)

并且不存储任何列表,只迭代一个生成器.此外,发电机中的"if"似乎是不必要的.

毕竟,线性序列应该只需要一个迭代器而不是两个迭代器.

与John Machin讨论后更新:

也许最优雅的解决方案是使用生成器函数来完全隐藏/抽象日期范围内的迭代:

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"))
Run Code Online (Sandbox Code Playgroud)

注意:为了与内置range()函数保持一致,此迭代到达之前停止end_date.因此,对于包容性迭代,请使用第二天,就像您一样range().

  • @Ber:我根本不喜欢它; 这太糟糕了.你已经有了迭代!通过将抱怨的构造包装在生成器中,您添加了更多的执行开销,并将用户的注意力转移到其他地方以阅读3-liner的代码和/或文档.-2 (9认同)
  • 如果你想要简洁,可以使用生成器表达式:`(start_date + datetime.timedelta(n)for n in range((end_date - start_date).days)) (8认同)
  • @John Machin:好的.然而,我确实在while循环上进行迭代,并显式增加某个计数器或值.交互模式更加pythonic(至少在我个人看来),也更通用,因为它允许表达迭代,同时隐藏迭代的完成细节. (6认同)
  • @John Machin:我不同意.关键不在于将行数减少到绝对最小值.毕竟,我们不是在这里谈论Perl.此外,我的代码只进行了一次迭代(这就是生成器的工作方式,但我猜你知道这一点).***我的观点是抽象重复使用和自解释代码的概念.我认为这比使用最短的代码更有价值. (6认同)
  • -1 ...当简单的while循环足够时,初步计算day_count并使用范围并不令人敬畏. (4认同)
  • @Ber:你没见过Sean Cavanagh的代码吗? (3认同)
  • @JohnMachin这是最狡猾的解决方案,真的.迭代变异值已经成为过去而非常含蓄.显式过度隐含,规则2,还记得吗? (3认同)
  • ```for n in range(int((end_date - start_date).days + 1)):```对于要包含的end_date (3认同)
  • 我喜欢这个,但我必须刷新我对相关导入的记忆:`from datetime import timedelta, date ; 开始日期 = 日期(2014,1,1) ; day_count = 15` - 之后,它为我执行。您可能会考虑为那些对 Python 生疏或陌生的人提供完整的示例。 (2认同)
  • 只是一点点改进: ```for n in range(int(start_date.day), int ((end_date - start_date).days), step):``` 这种方式现在可以通过步骤(如果想要跳转步骤)在交互中) (2认同)

Sea*_*agh 180

这可能更清楚:

from datetime import date, timedelta

start_date = date(2019, 1, 1)
end_date = date(2020, 1, 1)
delta = timedelta(days=1)
while start_date <= end_date:
    print (start_date.strftime("%Y-%m-%d"))
    start_date += delta
Run Code Online (Sandbox Code Playgroud)

  • 非常清晰短促,但如果要继续使用,效果不佳 (3认同)

nos*_*klo 156

使用dateutil图书馆:

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")
Run Code Online (Sandbox Code Playgroud)

这个python库有许多更高级的功能,一些非常有用,比如relative deltas-并且被实现为一个容易包含在项目中的单个文件(模块).

  • 请注意,此处for循环中的最终日期是*直到*包含*,而[Ber的答案](/ sf / ask / 74219561 /#answer-74223131)中的datedate方法的最后日期是*排除*。的“ end_date”。 (2认同)

fan*_*ous 63

熊猫一般非常适合时间序列,并且直接支持日期范围.

import pandas as pd
daterange = pd.date_range(start_date, end_date)
Run Code Online (Sandbox Code Playgroud)

然后,您可以遍历日期范围以打印日期:

for single_date in daterange:
    print (single_date.strftime("%Y-%m-%d"))
Run Code Online (Sandbox Code Playgroud)

它还有很多选项可以让生活更轻松.例如,如果您只想要工作日,则只需交换bdate_range.见http://pandas.pydata.org/pandas-docs/stable/timeseries.html#generating-ranges-of-timestamps

Pandas的强大功能实际上是它的数据帧,它支持矢量化操作(非常像numpy),可以非常快速,轻松地操作大量数据.

编辑:您也可以完全跳过for循环并直接打印,这样更容易,更有效:

print(daterange)
Run Code Online (Sandbox Code Playgroud)

  • “很像 numpy” - Pandas 是基于 numpy 构建的:P (4认同)
  • 您还可以使用 Floor、round、ceil 在确切日期开始: pd.date_range(start=start_date.floor('d'),end=end_date.floor('d'), freq = 'd') (2认同)

小智 15

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())
Run Code Online (Sandbox Code Playgroud)

这个功能比你严格要求的更多,支持负步骤等.只要你分解你的范围逻辑,那么你不需要单独的day_count,最重要的是当你从多个函数调用函数时,代码变得更容易阅读地方.


小智 11

为什么不尝试:

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
Run Code Online (Sandbox Code Playgroud)


Pat*_*ick 11

这是我能想到的最易读的解决方案.

import datetime

def daterange(start, end, step=datetime.timedelta(1)):
    curr = start
    while curr < end:
        yield curr
        curr += step
Run Code Online (Sandbox Code Playgroud)


小智 8

为了完整起见,Pandas 也有一个period_range用于处理越界时间戳的函数:

import pandas as pd

pd.period_range(start='1/1/1626', end='1/08/1627', freq='D')
Run Code Online (Sandbox Code Playgroud)


use*_*754 7

显示从今天开始的最后n天:

import datetime
for i in range(0, 100):
    print((datetime.date.today() + datetime.timedelta(i)).isoformat())
Run Code Online (Sandbox Code Playgroud)

输出:

2016-06-29
2016-06-30
2016-07-01
2016-07-02
2016-07-03
2016-07-04
Run Code Online (Sandbox Code Playgroud)

  • 我试过。编辑需要最少 6 个字符,但在这种情况下只需要添加 2 个字符,“(”和“)” (2认同)

Joh*_*hin 5

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)) 
Run Code Online (Sandbox Code Playgroud)


小智 5

for i in range(16):
    print datetime.date.today() + datetime.timedelta(days=i)
Run Code Online (Sandbox Code Playgroud)


Tor*_*Tor 5

Numpy的arange功能可以应用于日期:

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)
Run Code Online (Sandbox Code Playgroud)

使用astypeis来转换numpy.datetime64datetime.datetime对象数组.


Shi*_*eph 5

您可以使用 pandas 库简单可靠地生成两个日期之间的一系列日期

import pandas as pd

print pd.date_range(start='1/1/2010', end='1/08/2018', freq='M')
Run Code Online (Sandbox Code Playgroud)

您可以通过将 freq 设置为 D、M、Q、Y(每日、每月、每季度、每年)来更改生成日期的频率