创建没有闰日的熊猫 DatetimeIndex

时间:2021-07-09 12:08:03

标签: python pandas datetime

我想创建一个 Pandas DatetimeIndex,其中包含不同年份的闰日附近的一系列日期。有些年份是闰年,有些则不是。不过这里需要注意的是,我希望所有这些日期列表的长度相同。让我们看一些例子。

import pandas as pd
from datetime import timedelta

leap=pd.date_range('2020-02-27-12',pd.to_datetime('2020-02-27-12')+dt.timedelta(days=2),freq='6H')

DatetimeIndex(['2020-02-27 12:00:00', '2020-02-27 18:00:00',
               '2020-02-28 00:00:00', '2020-02-28 06:00:00',
               '2020-02-28 12:00:00', '2020-02-28 18:00:00',
               '2020-02-29 00:00:00', '2020-02-29 06:00:00',
               '2020-02-29 12:00:00'],
              dtype='datetime64[ns]', freq='6H')
len(leap)
9

在这种格式中避免闰日的最常见方法之一是简单地将它们从列表中删除。

leap=leap[(leap.day != 29) | (leap.month != 2)]
len(leap)
6

如果我只想删除闰日而不关心列表的具体长度,这很好用。

让我们为非闰年做同样的练习。

leap=pd.date_range('2021-02-27-12',pd.to_datetime('2021-02-27-12')+dt.timedelta(days=2),freq='6H')

DatetimeIndex(['2021-02-27 12:00:00', '2021-02-27 18:00:00',
               '2021-02-28 00:00:00', '2021-02-28 06:00:00',
               '2021-02-28 12:00:00', '2021-02-28 18:00:00',
               '2021-03-01 00:00:00', '2021-03-01 06:00:00',
               '2021-03-01 12:00:00'],
              dtype='datetime64[ns]', freq='6H')

len(leap)
9

自然,前两个具有相同的长度。然而,当我们从第一个列表中删除闰日时,我们现在可以看到长度不同,如 6 =/ 9。

这引出了一个终极问题:如何创建一个 Pandas 日期范围,该范围将采用第一个列表并跳过闰日并直接进入 3 月,将列表的长度保持在 9?


就上下文而言,我正在使用居中日期方法索引地理空间 (3D) 数据。例如,如果我想查看 1 月 5 日地球上某个时间点的数据,我会分析 1 月 5 日(1 月 3-7 日)前后 2 天的数据。通过长时间(> 30 年)的这种方法,我对我正在分析的变量有了更好的气候意义。为了做这个索引,我循环遍历日期并使用上面描述的熊猫日期范围方法。这是我用来解决索引地理空间 (3D) 数据问题的循环:

times=pd.date_range('1979-09-01','1980-04-30-18', freq='6H')
final_times = times[(times.day != 29) | (times.month != 2)]
years=np.arange(1979,2020,1)
for i in final_times:
    print(i)

    times_list=[]

    for j in years:
        times_forward=pd.date_range(i.replace(year=j),i.replace(year=j)+dt.timedelta(days=2), freq='6H')
        times_back=pd.date_range(i.replace(year=j)-dt.timedelta(days=2),i.replace(year=j)-dt.timedelta(hours=6), freq='6H')
        total_times=times_forward.union(times_back)
        times_list.append(total_times)
    combined_times=pd.DatetimeIndex([item for sublist in times_list for item in sublist]).sort_values()

按原样运行此代码时,围绕闰日的日期列表的长度比不接近闰日的日期列表的长度要短。

1 个答案:

答案 0 :(得分:1)

如果您只使用 DateOffset 对象,问题会为您处理:

>>> pd.date_range('2021-02-27-12', periods=9, freq='6H')
DatetimeIndex(['2021-02-27 12:00:00', '2021-02-27 18:00:00',
               '2021-02-28 00:00:00', '2021-02-28 06:00:00',
               '2021-02-28 12:00:00', '2021-02-28 18:00:00',
               '2021-03-01 00:00:00', '2021-03-01 06:00:00',
               '2021-03-01 12:00:00'],
              dtype='datetime64[ns]', freq='6H')
>>> pd.date_range('2021-02-27-12', periods=9, freq='6H') - pd.DateOffset(years=1)
DatetimeIndex(['2020-02-27 12:00:00', '2020-02-27 18:00:00',
               '2020-02-28 00:00:00', '2020-02-28 06:00:00',
               '2020-02-28 12:00:00', '2020-02-28 18:00:00',
               '2020-03-01 00:00:00', '2020-03-01 06:00:00',
               '2020-03-01 12:00:00'],
              dtype='datetime64[ns]', freq=None)

只要您参考的年份不是闰年,这就能满足您的需求。请注意,如果您以闰年为基础,那么您将有两次相同的日期,即您会将 28 日和 29 日与上一年或下一年的 28 日进行比较:

>>> pd.date_range('2020-02-27-12', periods=9, freq='6H')
DatetimeIndex(['2020-02-27 12:00:00', '2020-02-27 18:00:00',
               '2020-02-28 00:00:00', '2020-02-28 06:00:00',
               '2020-02-28 12:00:00', '2020-02-28 18:00:00',
               '2020-02-29 00:00:00', '2020-02-29 06:00:00',
               '2020-02-29 12:00:00'],
              dtype='datetime64[ns]', freq='6H')
>>> pd.date_range('2020-02-27-12', periods=9, freq='6H') + pd.DateOffset(years=1)
DatetimeIndex(['2021-02-27 12:00:00', '2021-02-27 18:00:00',
               '2021-02-28 00:00:00', '2021-02-28 06:00:00',
               '2021-02-28 12:00:00', '2021-02-28 18:00:00',
               '2021-02-28 00:00:00', '2021-02-28 06:00:00',
               '2021-02-28 12:00:00'],
              dtype='datetime64[ns]', freq=None)

我不确定这是否有问题 - 然后索引包含重复的项目,但这是我认为的唯一缺点。

基于此,这里有一个函数,它给出给定日期的每年日期(在范围内)的索引以进行比较:

def compare_times(datetime, periods=9, freq='6H', years=np.arange(1979, 2021)):
    ref_index = pd.DatetimeIndex([
        *pd.date_range(datetime, periods=1 + periods // 2, freq=f'-{freq}')[::-1],
        *pd.date_range(datetime, periods=1 + periods // 2, freq=freq)[1:]
    ])
    return pd.DatetimeIndex(np.concatenate([ref_index + pd.DateOffset(years=y -datetime.year) for y in years]))

现在没有问题了,只要您不使用 2 月 29 日调用该函数。如果您这样做,非闰年将返回相同数量的项目,但 2 月 28 日的条目重复:

>>> compare_times(pd.Timestamp(2020, 2, 29))
DatetimeIndex(['1979-02-28 00:00:00', '1979-02-28 06:00:00',
               '1979-02-28 12:00:00', '1979-02-28 18:00:00',
               '1979-02-28 00:00:00', '1979-02-28 06:00:00',
               '1979-02-28 12:00:00', '1979-02-28 18:00:00',
               '1979-03-01 00:00:00', '1980-02-28 00:00:00',
               ...
               '2019-03-01 00:00:00', '2020-02-28 00:00:00',
               '2020-02-28 06:00:00', '2020-02-28 12:00:00',
               '2020-02-28 18:00:00', '2020-02-29 00:00:00',
               '2020-02-29 06:00:00', '2020-02-29 12:00:00',
               '2020-02-29 18:00:00', '2020-03-01 00:00:00'],
              dtype='datetime64[ns]', length=378, freq=None)

在这里你可以看到 1979 年的复制品。