Передо мной встала задача прогноза исполнения поручений. В CRM фиксируются данные по поручению: кто назначил, когда, дата исполнения, подразделение, сорк исполнения и т.п..
Вот у меня возникла идея посчитать количество незакрытых актуальных поручений на дату назначения нового поручения. Ну т.е. руководитель назначает новое поручение, а машинка в этом момент считает, что вместе с этим поручением у Васи Иванова еще четыре незакрытых поручения. Вот такая задачка, и я хочу показать, как эта задача решается в Python.
Код
Вот у меня возникла идея посчитать количество незакрытых актуальных поручений на дату назначения нового поручения. Ну т.е. руководитель назначает новое поручение, а машинка в этом момент считает, что вместе с этим поручением у Васи Иванова еще четыре незакрытых поручения. Вот такая задачка, и я хочу показать, как эта задача решается в Python.
Код
Понадобится всего одна библиотека
import pandas as pdИ мы имеет вот примерно такой датафрейм
df = pd.DataFrame({'id': (1,1,1,2,2), 'begin': ('01.01.2018','01.02.2018', '01.03.2018', '01.01.2018', '01.02.2018'),
'end': ('01.02.2018','01.03.2018', '01.04.2018', '01.02.2018', '01.03.2018')})
df['begin']= pd.to_datetime(df['begin'])
df['end']= pd.to_datetime(df['end'])
df
begin end id
0 2018-01-01 2018-01-02 1
1 2018-01-02 2018-01-03 1
2 2018-01-03 2018-01-04 1
3 2018-01-01 2018-01-02 2
4 2018-01-02 2018-01-03 2
Где begin это дата начала проекта, end дата окончания проекта, а id - это исполнитель. Наша задача получить такой датафрейм begin end id new 0 2018-01-01 2018-01-02 1 3 1 2018-01-02 2018-01-03 1 2 2 2018-01-03 2018-01-04 1 1 3 2018-01-01 2018-01-02 2 2 4 2018-01-02 2018-01-03 2 1Где new это переменная, показывающая количество актуальных / незакрытых поручений на дату открытия нового проекта. В строке 0 у нас стоит 3, поскольку на дату 01 января 2018 у этого работника три незакрытых поручения. Цифру три можно получить, если мы возьмем даты окончания поручений по этому работнику, сравним их с датой начала данного поручения и посчитаем количество случаев, когда дата начала поручения меньше, чем дата окончания поручения.
Первое, с чего мы начинаем - сформируем по каждому работнику списки дат начала и окончания проектов
gr_end = df.groupby(['id'])['end'].apply(list) gr_begin = df.groupby(['id'])['begin'].apply(list)Получается вот так
gr_begin id 1 [2018-01-01 00:00:00, 2018-01-02 00:00:00, 201... 2 [2018-01-01 00:00:00, 2018-01-02 00:00:00] Name: begin, dtype: objectid в данном случае индекс и обозначает id работника, а каждому id соответствует объект типа list с датами в данном случае начала поручения.
Теперь самое сложное. По каждому работнику надо сравнить дату начала поручения со всеми датами окончания поручений и посчитать количество случаев, где дата начала поручения была меньше даты окончания.
Получился вот такой цикл
l = []
for i in gr_begin.index:
for x in gr_begin[i]:
cv = [j == 1 for j in gr_end[i] if j > x].count(False)
l.append(cv)
В написании циклов я не очень силен, поэтому, если вы увидите, что цикл можно улучшить, буду вам благодарен. Итог такой
l [3, 2, 1, 2, 1]Далее мы list l превращаем в новую переменную и окончательный датафрейм выглядит так
df['new'] = l
df
begin end id new
0 2018-01-01 2018-01-02 1 3
1 2018-01-02 2018-01-03 1 2
2 2018-01-03 2018-01-04 1 1
3 2018-01-01 2018-01-02 2 2
4 2018-01-02 2018-01-03 2 1
После написания кода я обратился на stackoverflow с просьбой предложить идеи по улучшению кода см. Create new variable based on groupby & value comparison
Мне предложили такой вариант
merged = df.merge(df.drop(columns='end'), on='id', suffixes=('', '_y'))
live_projects = merged[merged.begin<=merged.begin_y]
result = live_projects.groupby(['id','begin','end']).count().reset_index()
result.rename(columns={'begin_y':'new'}, inplace=True)
Вполне рабочий код.
Комментариев нет:
Отправить комментарий