Прежде чем мы рассмотрим ваши данные, давайте сначала посмотрим, как мы можем создать DatetimeIndex
для определенного дня каждого месяца.Поскольку обычный pd.date_range
с ежемесячной периодичностью принимает последний день каждого месяца, мы можем просто добавить фиксированное количество дней:
idx = pd.date_range('2018-04-01', '2018-07-01', freq='1M') + pd.DateOffset(days=16)
DatetimeIndex(['2018-05-16', '2018-06-16', '2018-07-16'],
dtype='datetime64[ns]', freq=None)
Теперь давайтевозьмем пример кадра данных, в котором пропущено около 16 th дней:
AAA BBB CCC
date
2012-04-16 44.48 28.48 17.65
2012-04-17 44.59 28.74 17.65
2012-05-15 45.79 29.07 17.80
2012-05-16 45.71 28.98 17.77
2012-05-17 45.44 28.81 17.79
2012-06-15 44.87 28.30 17.94
2012-06-17 44.95 28.50 17.98
2012-07-14 44.65 28.25 17.87
2012-07-17 44.55 28.75 17.75
Как вы упомянули, существует несколько способов выбора несоответствующих дней:идти назад, вперед или искать ближайший без предпочтений. Вы должны рассмотреть, что наиболее уместно в контексте вашего проекта. Ниже приведено решение, которое придерживается функциональности Pandas и избегает пользовательских lambda
функций.
Определите фрейм данных с помощью DatetimeIndex
Сначала создайте фрейм данных с указанием только обязательных индексов:
offset = pd.DateOffset(days=16)
start_date = df.index[0]-pd.DateOffset(months=1)
idx = pd.date_range(start_date, df.index[-1], freq='1M') + offset
df_idx = pd.DataFrame(index=idx)
Обратите внимание, нам нужно вычесть месяц из аргумента начала, чтобы первый месяц не был пропущенпосле добавления 16 дней.Теперь вы можете использовать pd.merge_asof
с различными вариантами: -
Совпадение назад / вперед / ближайший через merge_asof
Укажите direction
аргумент как 'backward'
(по умолчанию), 'forward'
или 'nearest'
в зависимости от ситуации.Например, используя 'forward'
:
print(pd.merge_asof(df_idx, df, left_index=True, right_index=True, direction='forward'))
AAA BBB CCC
2012-04-16 44.48 28.48 17.65
2012-05-16 45.71 28.98 17.77
2012-06-16 44.95 28.50 17.98
2012-07-16 44.55 28.75 17.75
Теперь этого может быть достаточно для ваших нужд.
Редактировать: Если вы хотите сохранить индексиз кадра данных вы можете изменить направление слияния и использовать 'backward'
вместо 'forward'
:
res = pd.merge_asof(df.reset_index(),
df_idx.reset_index().rename(columns={'index': 'date_idx'}),
left_on='date', right_on='date_idx', direction='backward')
res['diff'] = (res['date'] - res['date_idx']).dt.days.abs()
grouper = res['date'].dt.strftime('%Y-%m')
res = res[res['diff'] == res.groupby(grouper)['diff'].transform('min')]
print(res)
date AAA BBB CCC date_idx diff
0 2012-04-16 44.48 28.48 17.65 2012-04-16 0
3 2012-05-16 45.71 28.98 17.77 2012-05-16 0
6 2012-06-17 44.95 28.50 17.98 2012-06-16 1
8 2012-07-17 44.55 28.75 17.75 2012-07-16 1