Начните с определения смещения до конца рабочего месяца:
offs = pd.offsets.BMonthEnd(0)
Затем определите функцию для вычисления последнего рабочего дня для текущей даты:
def dateCorr(dat : pd.Timestamp) -> pd.Timestamp:
dFwd = offs.rollforward(dat)
return dFwd if dFwd.month == dat.month else offs.rollback(dat)
Для целей тестирования , сохраните "исправленные" даты как новый столбец:
df['LastBDay'] = df.curr_date.apply(dateCorr)
, чтобы вы могли сравнивать исходные даты с новыми датами.
Конечно, в окончательной версии кода замените LastBDay с curr_date , чтобы перезаписать существующий столбец.
Другое, гораздо более быстрое решение
Как вы, наверное, знаете, Numpy работает намного быстрее, чем Pandas, поэтому я искал способ выполнить вашу задачу, основываясь исключительно на Numpy.
В результате я нашел гораздо более быстрое решение:
df['LastBDay'] = np.busday_offset(
df.curr_date.values.astype('M8[M]') + np.timedelta64(1, 'M'), -1, roll='forward')
Шаги:
df.curr_date.values.astype('M8[M]')
- Преобразование curr_date в дату с разрешением месяц . + np.timedelta64(1, 'M')
- Перенаправить эту дату на один месяц. np.busday_offset(
- Найти рабочий день, начиная с первого аргумента. -1
- на 1 день назад. roll='forward'
- Это хитрая деталь. Поскольку смещение составляет -1 , направление вращения - назад . Но передача назад здесь иногда откатывает дату еще день назад. Поэтому, чтобы избежать этого другого сдвига, этот параметр должен быть просто вперед .
А теперь, насколько быстрее это решение.
Использование % timeit Я измерил время выполнения предыдущего решения для выборки из 16 000 строк, получив 2,47 с .
Но время выполнения этого (второго) решения составляет 12,8 мс - в 190 раз быстрее .
Это намного лучшая скорость выполнения также является результатом того факта, что второе решение использует векторизованные операции вместо приложения функции в каждую строку.