Применение функции по расчёту рабочих дней (5 дневка) в датафрейме между двумя столбцами с датами

Пытаюсь решить простецкую задачку, но никак не получается. Исходные данные Имеется датафрейм, состоящий из столбцов Работа-дата начала-дата завершения- и. Цель сформировать дополнительный столбец с числом рабочих дней (5-дневка) внутри диапазона (дата начала - дата завершения)

Сам датафрейм

index,Work,Start_date,End_date
0,Каталог изделия,2022-12-15 00:00:00,2023-02-15 00:00:00
1,Регламент СО,2022-10-01 00:00:00,2023-04-30 00:00:00
2,Лопатка,2023-02-01 00:00:00,2023-03-15 00:00:00
3,Ротор,2023-03-01 00:00:00,2023-05-31 00:00:00
4,Комплект Запчастей,2023-03-01 00:00:00,2023-04-30 00:00:00

Расчёт вне датафрейма на двух переменных удалось реализовать по найденному алгоритму:

import pandas as pd
from datetime import date

start_date = date.fromisoformat('2023-01-06')
end_date =  date.fromisoformat('2023-01-11')

def clean_dates(df):
dates = pd.date_range(start_date, end_date, freq='D')
res = 1+len(dates\[(dates.date\>=start_date)&(dates.date\<end_date)&(dates.weekday\<5)\])

Попытка адаптировать его под датафрейм не увенчалась успехом:

def clean_dates(df, x):
  dates = pd.date_range(x df["Start_date"], x df["End_date"], freq='D')
  res = 1+len(dates[(dates.date>=x df["Start_date"])&(dates.date<x df["End_date"])&(dates.weekday<5)])
  return(df, x)

df["cwd"] = df.apply(clean_dates, axis=1)

в итоге пришлось колхозить:

df["range"] = df.apply(lambda x: 21*len(pd.date_range(start=x["Start_date"], end=x["End_date"], freq="M")
                       .date.tolist()), axis=1)  #determination of work days (21 - average work days per month)

Ответы (1 шт):

Автор решения: strawdog

Во-первых, среднее количество рабочих дней здесь не подойдет - это как средняя температура по больнице, для точных подсчетов не годится. Во-вторых, есть готовые средства pandas, позволяющие считать рабочие дни (другое дело, что, например, российские праздники там вряд ли учитываются. я бы предложил сделать так:

res = df.copy()
res['days'] = [pd.bdate_range(start, end) for start, end in zip(res['Start_date'], res['End_date'])]
# ^^^ обратите внимание на bdate_range - в отличие от date_range
# здесь учитываются "бизнес-дни"
res = res.explode("days")
res = res.groupby("Work")["days"].count()
df = df.join(res.drop(columns="days"), on="Work")

получим df:

                     Work           Start_date             End_date  days
index                                                                    
0         Каталог изделия  2022-12-15 00:00:00  2023-02-15 00:00:00    45
1            Регламент СО  2022-10-01 00:00:00  2023-04-30 00:00:00   150
2                 Лопатка  2023-02-01 00:00:00  2023-03-15 00:00:00    31
3                   Ротор  2023-03-01 00:00:00  2023-05-31 00:00:00    66
4      Комплект Запчастей  2023-03-01 00:00:00  2023-04-30 00:00:00    43
→ Ссылка