如何在 pandas date_range 方法中包含结束日期?

Posted

技术标签:

【中文标题】如何在 pandas date_range 方法中包含结束日期?【英文标题】:How to include end date in pandas date_range method? 【发布时间】:2016-10-19 19:30:02 【问题描述】:

pd.date_range('2016-01', '2016-05', freq='M', ).strftime('%Y-%m'),上个月是2016-04,但我期待它是2016-05。在我看来,这个函数的行为类似于 range 方法,其中 end 参数不包含在返回数组中。

有没有办法在不处理结束月份的字符串的情况下将结束月份包含在返回数组中?

【问题讨论】:

date_range() 参数似乎还是有点棘手github.com/pandas-dev/pandas/issues/16354 【参考方案1】:

在数据框中使用日期时间对象时,我遇到了类似的问题。我将通过 .min() 和 .max() 函数设置边界,然后使用 pd.date_range 函数填充缺失的日期。不幸的是,返回的 list/df 缺少最大值。

我找到了两个解决方法:

1) 在 pd.date_range 函数中添加“closed = None”参数。这在下面的示例中有效;但是,仅使用数据框时它对我不起作用(不知道为什么)。

2) 如果选项 #1 不起作用,那么您可以使用 datetime.timedelta() 函数添加一个额外的单位(在这种情况下为一天)。在下面的情况下,它被一天索引,但如果 date_range 函数没有给你完整的范围,它可以为你工作。

import pandas as pd
import datetime as dt 

#List of dates as strings
time_series = ['2020-01-01', '2020-01-03', '2020-01-5', '2020-01-6', '2020-01-7']

#Creates dataframe with time data that is converted to datetime object 
raw_data_df = pd.DataFrame(pd.to_datetime(time_series), columns = ['Raw_Time_Series'])

#Creates an indexed_time list that includes missing dates and the full time range

#Option No. 1 is to use the closed = None parameter choice. 
indexed_time = pd.date_range(start = raw_data_df.Raw_Time_Series.min(),end = raw_data_df.Raw_Time_Series.max(),freq='D',closed= None)
print('indexed_time option #! = ', indexed_time)

#Option No. 2 if the function allows you to extend the time by one unit (in this case day) 
#by using the datetime.timedelta function to get what you need. 
indexed_time = pd.date_range(start = raw_data_df.Raw_Time_Series.min(),end = raw_data_df.Raw_Time_Series.max()+dt.timedelta(days=1),freq='D')
print('indexed_time option #2 = ', indexed_time)

#In this case you over index by an extra day because the date_range function works properly
#However, if the "closed = none" parameters doesn't extend through the full range then this is a good work around 

【讨论】:

【参考方案2】:

这个问题的解释是pd.to_datetime()函数默认将'%Y-%m'日期字符串转换为月份的第一天日期时间,或者'%Y-%m-01'

>>> pd.to_datetime('2016-05')
Timestamp('2016-05-01 00:00:00')
>>> pd.date_range('2016-01', '2016-02')
DatetimeIndex(['2016-01-01', '2016-01-02', '2016-01-03', '2016-01-04',
               '2016-01-05', '2016-01-06', '2016-01-07', '2016-01-08',
               '2016-01-09', '2016-01-10', '2016-01-11', '2016-01-12',
               '2016-01-13', '2016-01-14', '2016-01-15', '2016-01-16',
               '2016-01-17', '2016-01-18', '2016-01-19', '2016-01-20',
               '2016-01-21', '2016-01-22', '2016-01-23', '2016-01-24',
               '2016-01-25', '2016-01-26', '2016-01-27', '2016-01-28',
               '2016-01-29', '2016-01-30', '2016-01-31', '2016-02-01'],
              dtype='datetime64[ns]', freq='D')

然后一切都随之而来。指定 freq='M' 包括 2016-01-01 和 2016-05-01 之间的月末,这是您收到的列表,不包括 2016-05-31。但是指定月份开始 'MS' 就像第二个答案提供的那样,包括 2016-05-01,因为它属于该范围。 pd.date_range() 默认行为与 range 方法不同,因为它包含结尾。来自the docs:

close 控制是否包含边界上的开始和结束。默认包括两端的边界点。

【讨论】:

【参考方案3】:

对于后来的人群。您也可以尝试使用 Month-Start 频率。

>>> pd.date_range('2016-01', '2016-05', freq='MS', format = "%Y-%m" )
DatetimeIndex(['2016-01-01', '2016-02-01', '2016-03-01', '2016-04-01',
               '2016-05-01'],
              dtype='datetime64[ns]', freq='MS')

【讨论】:

【参考方案4】:

您可以在初始化date_range 后使用.union 添加下一个逻辑值。它应该适用于任何频率:

d = pd.date_range('2016-01', '2016-05', freq='M')
d = d.union([d[-1] + 1]).strftime('%Y-%m')

或者,您可以使用period_range 代替date_range。根据您打算做什么,这可能不是正确的使用方法,但它可以满足您的问题:

pd.period_range('2016-01', '2016-05', freq='M').strftime('%Y-%m')

无论哪种情况,结果输出都符合预期:

['2016-01' '2016-02' '2016-03' '2016-04' '2016-05']

【讨论】:

感谢 period_range,这正是我想要的。 .union 也是类似问题的解决方案:您希望每月间隔,包括您的端点,但您的开始和结束不落在​​月份的开始/结束,例如start=pd.to_datetime('2016-01-05')finish=pd.to_datetime('2016-05-13')d=date_range(start, finish,freq='M').union([start, finish])。它甚至会为您排序索引。【参考方案5】:

我不这么认为。 您需要添加 (n+1) 边界

   pd.date_range('2016-01', '2016-06', freq='M' ).strftime('%Y-%m')

开始和结束日期严格包含在内。所以不会 如果指定,则生成这些日期之外的任何日期。 http://pandas.pydata.org/pandas-docs/stable/timeseries.html

无论哪种方式,您都必须手动添加一些信息。我相信只增加一个月并不是很多工作。

【讨论】:

freq='D' 时,文档中的这句话是正确的,每月一次,它不适用于结束日期。 好的。它不会改变您需要添加边界的事实:)【参考方案6】:

一种不用自己弄清楚月末的方法。

pd.date_range(*(pd.to_datetime(['2016-01', '2016-05']) + pd.offsets.MonthEnd()), freq='M')

DatetimeIndex(['2016-01-31', '2016-02-29', '2016-03-31', '2016-04-30',
           '2016-05-31'],
          dtype='datetime64[ns]', freq='M')

【讨论】:

有了这个解决方案,我不需要搞砸天和 (n+1) 个月。【参考方案7】:

date_range 调用中指定日期时包括日期

pd.date_range('2016-01-31', '2016-05-31', freq='M', ).strftime('%Y-%m')

array(['2016-01', '2016-02', '2016-03', '2016-04', '2016-05'], 
      dtype='|S7')

【讨论】:

如果你手动加一天,那你不觉得还不如再加一个月 没有。我可以想象根据情况选择其中任何一种的需要和理由。

以上是关于如何在 pandas date_range 方法中包含结束日期?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 pandas.date_range() 在指定的开始日期和结束日期之间获取具有 n 个指定周期(相等)的时间序列

date_range

Python数据分析pandas日期范围date_range

具有特定时间范围的 Pandas date_range

如何反向执行 date_range?

为什么date_range的结果与索引DataFrame Pandas日期的[]不同?