如何使用pandas.date_range()生成一个在结束参数后面没有时间戳的范围?

时间:2015-03-20 15:38:53

标签: python datetime pandas date-range

我正在使用pandas在python中工作。我在一天内查询独特用户的外部数据源(例如每30天或每2天或每7天一次的唯一用户)。查询需要句点开始,句点结束和间隔(天数)。

例如,为了获得jan 1st的唯一用户,params是:

  • 开始时间:2015-01-01
  • end:2015-01-01
  • 间隔:1

1月1日至1月1日(3天分组):

  • 开始时间:2015-01-01
  • end:2015-01-03
  • 间隔:3

有时候我也需要多个时期。在所有情况下,一段时间可以在结束日期之后结束,但永远不会在结束之后开始。

例如,对于1月1日到1月1日的独特30天:

  • 开始时间:2015-01-01
  • end:2015-01-01
  • 间隔:30

理想情况下,我会使用pandas.period_range(),但由于它不接受freq参数中的多个,我转向pandas.date_range()。我正在迭代日期范围内的时间戳,如下所示:

import pandas
start_date = "2015-01-01"
end_date = "2015-01-03"
interval = 3

for timestamp in pandas.date_range(start_date , end_date , freq=str(interval)+"D"):
    period_start = timesteamp.date()
    period_end = period_start + datetime.timedelta(days=interval)
    # query with period_start, period_end, interval

因此,当pandas生成的范围的最后一个元素位于end参数之后时,它会产生一个不必要的循环和我想要避免的不需要的查询。

问题是这样的调用:

pandas.date_range("2015-01-01", "2015-01-03", freq="3D")

返回:

<class 'pandas.tseries.index.DatetimeIndex'>
[2015-01-01, 2015-01-04]
Length: 2, Freq: 3D, Timezone: None

最后一个元素2015-01-04在2015-01-03结束限制之后。当频率短于或长于实际期间(在这种情况下为2天或4天)时,不会发生这种情况:

>>> pandas.date_range("2015-01-01", "2015-01-03", freq="2D")
<class 'pandas.tseries.index.DatetimeIndex'>
[2015-01-01, 2015-01-03]
Length: 2, Freq: 2D, Timezone: None

>>> pandas.date_range("2015-01-01", "2015-01-03", freq="4D")
<class 'pandas.tseries.index.DatetimeIndex'>
[2015-01-01]
Length: 1, Freq: 4D, Timezone: None

我试图了解close参数,但描述对我来说有点神秘。这三个值中没有一个(“右”,“左”,“无”)似乎给出了我所追求的东西。

我测试了3个可能的closed=值来观察不同的输出:

>>> pandas.date_range("2015-01-01", "2015-01-03", freq="3D", closed="left")
<class 'pandas.tseries.index.DatetimeIndex'>
[2015-01-01]
Length: 1, Freq: 3D, Timezone: None

这似乎是理想的结果。但是在freq比起始和结束之间的时间长的情况下,它返回一个包含0个元素的范围。

>>> pandas.date_range("2015-01-01", "2015-01-01", freq="10D", closed="left")
<class 'pandas.tseries.index.DatetimeIndex'>
Length: 0, Freq: 10D, Timezone: None

我希望:

<class 'pandas.tseries.index.DatetimeIndex'>
[2015-01-01]
Length: 1, Freq: 10D, Timezone: None

我唯一一次看到结果为空的情况是freq的乘数是否为0(即“0D”,“0H”,“0W”)。这已经引发了错误,所以在practice date_range()中应始终至少返回它的起始值。如果end参数出现在频率结束之前,则范围将只包含一个时间戳。

2 个答案:

答案 0 :(得分:0)

您可以设置多个date_range并使用pandas.DatetimeIndex.union联合所有日期时间

答案 1 :(得分:0)

我认为你的间隔语义有点困惑,熊猫在这里做了一些合理的事情。

考虑你的最后一个例子:

>>> pandas.date_range("2015-01-01", "2015-01-01", freq="10D", closed="left")

你写的是你期望的:

<class 'pandas.tseries.index.DatetimeIndex'>
[2015-01-01]
Length: 1, Freq: 10D, Timezone: None

考虑2015-01-04之类的日期。一方面,这将是 in 此DatetimeIndex中的第一个(唯一)时间间隔。另一方面,此日期将在您提供的结束日期之后下降,这似乎与您原始date_range电话中的时间间隔规格相矛盾。

或换句话说,从1月1日到1月2日间隔30天是什么意思?

如果您希望区间索引自动扩展到包含开始日期加间隔长度的结束日期,我认为您需要编写一个函数来执行此操作。

def nonempty_date_range(start, end, freq, closed=None):
    """ Return a pandas.DatetimeIndex containing at least one interval.  In some cases, the interval will extend beyond 'end'. """
    start = pandas.to_datetime(start)
    end = pandas.to_datetime(end)
    length = pandas.to_timedelta(freq)

    end = max(end, start + length)

    return pandas.date_range(start=start, end=end, freq=freq, closed=closed)

结果:

# Example from question
>>> pandas.date_range("2015-01-01", "2015-01-01", freq="10D", closed="left")
DatetimeIndex([], dtype='datetime64[ns]', freq='10D')

以上功能:

>>> nonempty_date_range("2015-01-01", "2015-01-01", freq="10D", closed="left")
DatetimeIndex(['2015-01-01'], dtype='datetime64[ns]', freq='10D')