pandas: расчет дат начала и времени выполнения задач
Я решил распределить выполнение определенных задач по дням в зависимости от времени, затрачиваемого на задачи. В день выделяется 7 часов, задачи же могут занимать разное время, но только в целых числах (1 ч, 2 ч, 3 ч и т.д.).
У меня имеется data frame, где содержатся названия задач, дата начала, дата конца и время выполнения задачи. Как автоматически заполнить даты в data frame? Уже десятки вариантов перебрал.
Основная трудность для меня состоит в том, чтобы учитывать время задач. Например, длится она 16 часов. Значит, на ее выполнение понадобится 2 полных дня (по 7 часов) и 2 часа от третьего дня. Тогда для третьего дня остается свободных 5 часов, в которые можно начать работу над следующей задачей.
Думаю собрать словарь, где ключами выступает название задачи, а значениями - даты в зависимости от того, сколько длится одна задача. Словарь можно собрать из 2 списков, в которых хранятся даты и время на задачи. Возможно, думаю не в ту сторону, но голова уже отказывается выдавать решения, прошу помочь)
Так выглядит data frame:
Title Start Date End Date Description
1 Handover 16
2 Project Setup 8
3 Add localization two languages (English, Spanish) 6
Ответы (1 шт):
Не совсем понятно, но если вам просто нужно получить конечную дату выполнения задач, имея начальную дату и количество часов, отпущенных на каждый день, то можно сделать так:
import pandas as pd
hours_per_day = 7
df = pd.DataFrame({"task":["task1", "task2", "task3"],
"start date":["2021-07-02", "2021-07-01", "2021-07-03"],
"duration":[16, 8, 6]})
df["start date"] = pd.to_datetime(df["start date"])
получаем исходный датафрейм:
task start date duration
0 task1 2021-07-02 16
1 task2 2021-07-01 8
2 task3 2021-07-03 6
дальше просто вычисляем дельту:
df["end date"] = df["start date"] + pd.to_timedelta(df["duration"]//hours_per_day, "d")
df:
task start date duration end date
0 task1 2021-07-02 16 2021-07-04
1 task2 2021-07-01 8 2021-07-02
2 task3 2021-07-03 6 2021-07-03
UPDATE
после уточнения автора вопроса, выяснилось, что по сути необходимо построить последовательный work-flow для непрерывного выполнения задач. Это можно сделать как-нибудь так:
import numpy as np
import pandas as pd
# исходные данные
durations = [4, 8, 2, 16, 5]
hours_per_day = 7
start_date = "2021-07-01"
arr = np.array(durations)
bins = [0]
bins.extend([x[-1] for x in
np.array_split(np.arange(np.sum(arr)), np.ceil(np.sum(arr)/hours_per_day))])
# [0, 6, 13, 20, 27, 34]
totals = np.cumsum(arr)
# [ 4 12 14 30 35]
idx = np.digitize(totals, bins, right=True)-1
# [0 1 2 4 5]
start_deltas = np.pad(idx, (1, 0))[:-1]
# [0 0 1 2 4] <-- искомые дельты
Далее конструируем датафрейм:
df = pd.DataFrame({"task":[f"task{x}" for x in range(len(durations))],
"duration": durations,
"delta":start_deltas})
df["start_date"] = start_date
df["start_date"] = pd.to_datetime(df["start_date"])
df["start_date"] = df["start_date"] + pd.to_timedelta(df["delta"], "d")
df["end_date"] = df["start_date"] + pd.to_timedelta(df["duration"]//hours_per_day, "d")
получаем df:
task duration delta start_date end_date
0 task0 4 0 2021-07-01 2021-07-01
1 task1 8 0 2021-07-01 2021-07-02
2 task2 2 1 2021-07-02 2021-07-02
3 task3 16 2 2021-07-03 2021-07-05
4 task4 5 4 2021-07-05 2021-07-05
Идея заключается в том, чтобы разбить всю ленту часов выполнения задач на бины (отрезки), ограниченные точками начала и конца рабочих часов в течение одного дня. соответственно, начальные и конечные точки этих задач в указанных бинах и будут соответствовать начальным и конечным датам.
UPDATE 2*
Если нужно из workflow исключить нерабочие дни, то можно сделать так:
from pandas.tseries.offsets import *
# вместо последних двух преобразований сделать:
df["start_date"] = df.apply(lambda x: x["start_date"] + BDay(pd.to_timedelta(x["delta"], "d").days), axis=1)
df["end_date"] = df.apply(lambda x: x["start_date"] + BDay(pd.to_timedelta(x["duration"]//hours_per_day, "d").days), axis=1)
получим df:
task duration delta start_date end_date
0 task0 4 0 2021-07-01 2021-07-01
1 task1 8 0 2021-07-01 2021-07-02
2 task2 2 1 2021-07-02 2021-07-02
3 task3 16 2 2021-07-05 2021-07-07
4 task4 5 4 2021-07-07 2021-07-07