Вы уже рассматривали возможность использования pandas.merge_asof
для этого?
Я мог бы представить, что map
и apply
с лямбда-функциями не могут быть выполнены так эффективно.
ОБНОВЛЕНИЕ: Ах, извините, я только что прочитал, что вам нужно только логическое значение, если между ними есть какие-то праздники, это делает это намного проще.Если этого достаточно, вам просто нужно выполнить шаги 1-5, а затем сгруппировать DataFrame, который является результатом шага 5, по дате начала / окончания и использовать count в качестве статистической функции, чтобы количество выходных в диапазонах.Этот результат можно присоединить к исходному набору данных, аналогично шагу 8, описанному ниже.Затем заполните остальные значения fillna(0)
.Сделайте что-то вроде joined_df['includes_holiday']= joined_df['joined_count_column']>0
.После этого вы можете, если хотите, снова удалить joined_count_column
из своего DataFrame.
Если вы используете pandas_merge_asof
, вы можете выполнить эти шаги (шаги 6 и 7 необходимы только в том случае, если вам нужно иметьвсе выходные между началом и концом также в вашем DataFrame результата, а не только в логических значениях):
- Загрузите записи вашего отпуска в DataFrame и проиндексируйте их по дате.Праздники должны быть по одной дате в строке (сохранение диапазонов, например, на Рождество с 24 по 26 в один ряд, сделало бы это намного более сложным).
- Создайте копию вашего фрейма данных, используя только столбцы даты начала и окончания.ОБНОВЛЕНИЕ: каждое начало, дата окончания должны встречаться только один раз.Например, используя groupby.
- Используйте
merge_asof
с разумным значением допуска (если вы присоединяетесь в начале периода, используйте direction='forward'
, если вы используете дату окончания, используйте direction='backward'
и how='inner'
. - В результате у вас есть объединенный DataFrame с начальным, конечным столбцами и столбцом даты из вашего кадра данных выходных. Вы получаете только записи, для которых был найден выходной с заданным допуском, но позже выМожно объединить эти данные с исходным DataFrame. Возможно, теперь у вас будут дубликаты исходных записей.
- Затем проверьте объединенные выходные для ваших записей с индексаторами, сравнив их со столбцом начала и конца, и удалите выходные., которые не являются промежуточными.
- Сортируйте кадр данных, полученный вами на шаге 5 (используйте что-то вроде
df.sort_values(['start', 'end', 'holiday'], inplace=True)
. Теперь вам нужно вставить числовой столбец, который нумерует выходные дни между периодами (те, которые вы получили после шага 5) от 1 до ... (для каждого периода, начинающегося с 1). Необходимо использовать unstack на следующем шаге дляполучите выходные данные в столбцах. - Добавьте индекс в свой фрейм данных на основе даты начала периода, даты окончания периода и столбца подсчета, который вы вставили в шаге 6. Используйте
df.unstack(level=-1)
в фрейме данных, который вы подготовили в шагах 1-7.Теперь у вас есть сжатый DataFrame с вашими исходными периодами и праздничными днями, расположенными по столбцам. - Теперь вам нужно только объединить этот DataFrame с вашими исходными данными, используя
original_df.merge(df_from_step7, left_on=['start', 'end'], right_index=True, how='left')
Результатом этого является файл с вашими исходными данными, содержащий диапазоны дат, и для каждого диапазона дат выходные дни, которые лежат между периодами, хранятся в отдельных столбцах, каждый за данными.Грубо говоря, нумерация на шаге 6 назначает праздничные дни столбцам и приводит к тому, что праздничные дни всегда назначаются столбцам справа налево (у вас не будет выходных в столбце 3, если столбец 1 пуст).
Шаг 6., вероятно, также немного сложен, но вы можете сделать это, например, добавив серию, заполненную диапазоном, а затем исправив ее, чтобы нумерация начиналась с 0 или 1 в каждой группе с помощью shift
или группировка по началу, завершается aggregate({'idcol':'min')
и присоединяется к результату обратно, чтобы вычесть его из значения, назначенного в диапазоне-последовательности.
В целом, я думаю, что это звучит сложнее, чем есть, и этодолжно быть выполнено довольно эффективно.Особенно, если ваши периоды не так велики, потому что после шага 5 ваш результирующий набор должен быть намного меньше исходного фрейма данных, но даже если это не так, он все равно должен быть достаточно эффективным, поскольку он может использовать скомпилированный код.