Диапазон дат Pandas ежемесячно в определенный день месяца

#python #datetime #pandas

#python #дата и время #pandas

Вопрос:

Я знаю, что в Pandas вы можете использовать смещения привязки для указания более сложных запросов: http://pandas.pydata.org/pandas-docs/stable/timeseries.html#anchored-offset

Я хочу указать диапазон дат таким образом, чтобы он был ежемесячным в n-й день каждого месяца. Какой синтаксис лучше всего подходит для этого? Я представляю что-то похожее на это, которое определяет повторение каждые 2 недели в пятницу:

 schedule = pd.date_range(start=START_STR, periods=26, freq="2W-FRI")
 

Комментарии:

1. Даже если для этого нет готовой pandas функции, вы должны быть в состоянии написать простую вспомогательную функцию для этого pd.date_range и pd.DatetimeIndex . Взгляните на мой пример сценария .

Ответ №1:

IIUC вы можете сделать это таким образом:

 In [18]: pd.DataFrame(pd.date_range('2016-01-01', periods=10, freq='MS')   pd.DateOffset(days=26), columns=['Date'])
Out[18]:
        Date
0 2016-01-27
1 2016-02-27
2 2016-03-27
3 2016-04-27
4 2016-05-27
5 2016-06-27
6 2016-07-27
7 2016-08-27
8 2016-09-27
9 2016-10-27
 

ОБНОВЛЕНИЕ: для учета разного количества дней в месяцах и високосных годах:

 def month_range(start, periods=12):
    rng = pd.date_range(pd.Timestamp(start)-pd.offsets.MonthBegin(),
                        periods=periods,
                        freq='MS')
    ret = (rng   pd.offsets.Day(pd.Timestamp(start).day-1)).to_series()
    ret.loc[ret.dt.month > rng.month] -= pd.offsets.MonthEnd(1)
    return pd.DatetimeIndex(ret)
 

Примеры:

 In [202]: month_range('2016-01-27', 12)
Out[202]:
DatetimeIndex(['2016-01-27', '2016-02-27', '2016-03-27', '2016-04-27', '2016-05-27', '2016-06-27', '2016-07-27', '2016-08-27',
               '2016-09-27', '2016-10-27', '2016-11-27', '2016-12-27'],
              dtype='datetime64[ns]', freq=None)

In [203]: month_range('2020-01-31', 12)
Out[203]:
DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31', '2020-04-30', '2020-05-31', '2020-06-30', '2020-07-31', '2020-08-31',
               '2020-09-30', '2020-10-31', '2020-11-30', '2020-12-31'],
              dtype='datetime64[ns]', freq=None)

In [204]: month_range('2019-01-29', 12)
Out[204]:
DatetimeIndex(['2019-01-29', '2019-02-28', '2019-03-29', '2019-04-29', '2019-05-29', '2019-06-29', '2019-07-29', '2019-08-29',
               '2019-09-29', '2019-10-29', '2019-11-29', '2019-12-29'],
              dtype='datetime64[ns]', freq=None)
 

Комментарии:

1. Аккуратное решение, но не учитывает разное количество дней для каждого месяца и високосных лет, например, для смещения дня на 29 или выше.

2. @mloning, спасибо, что указали на это — я попытался улучшить свой ответ…

Ответ №2:

Нет необходимости заново изобретать колесо. Польза DateOffset от панд:

 import pandas as pd
from pandas.tseries.offsets import DateOffset
from datetime import date

date1 = date(2019,1,29)
pd.date_range(date1, periods=12, freq=DateOffset(months=1))
 

Выходной сигнал:

 DatetimeIndex(['2019-01-29', '2019-02-28', '2019-03-28', '2019-04-28',
               '2019-05-28', '2019-06-28', '2019-07-28', '2019-08-28',
               '2019-09-28', '2019-10-28', '2019-11-28', '2019-12-28'],
              dtype='datetime64[ns]', freq='<DateOffset: months=1>')
 

Комментарии:

1. Но как только оно уменьшается до 28, остальные даты также становятся 28. То же самое происходит и с 31. Если это 31 января, то остальные месяцы сокращаются до 28

2. pd.date_range(date1, периоды = 12, частота=pd.DateOffset(месяцы = 1))

Ответ №3:

Редактировать: Принятый ответ изначально не учитывал разное количество дней в месяцах и високосных годах. Вот еще альтернативная функция для решения проблемы:

 import pandas as pd

def month_range_day(start=None, periods=None):
    start_date = pd.Timestamp(start).date()
    month_range = pd.date_range(start=start_date, periods=periods, freq='M')
    month_day = month_range.day.values
    month_day[start_date.day < month_day] = start_date.day
    return pd.to_datetime(month_range.year*10000 month_range.month*100 month_day, format='%Y%m%d')
 

Пример 1:

 start_date = '2020-01-31'
month_range_day(start=start_date, periods=12)
 

Вывод:

 DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31', '2020-04-30',
               '2020-05-31', '2020-06-30', '2020-07-31', '2020-08-31',
               '2020-09-30', '2020-10-31', '2020-11-30', '2020-12-31'],
              dtype='datetime64[ns]', freq=None) 
 

Пример 2:

 start_date = '2019-01-29'
month_range_day(start=start_date, periods=12)
 

Вывод:

 DatetimeIndex(['2019-01-29', '2019-02-28', '2019-03-29', '2019-04-29',
               '2019-05-29', '2019-06-29', '2019-07-29', '2019-08-29',
               '2019-09-29', '2019-10-29', '2019-11-29', '2019-12-29'],
              dtype='datetime64[ns]', freq=None)