Как прохождение электронного курса влияет на выживаемость персонала
Зачем это все?
Приветствую, коллеги. В данном посте я хочу рассказать вам о том, как я решал (и еще решаю) задачу анализа выживаемости персонала в зависимости от того, пройден или нет электронный курс. Главный гость нашего набора данных - электронный вводный курс, который назначается 80% сотрудников компании.
Может возникнуть вопрос: "Зачем?". На деле в компании достаточно много дистанционных программ обучения и хочется понимать насколько они влияют на сотрудников и может ли данный вид обучения влиять на продолжительность работы сотрудников.
И так, гипотеза следующая: сотрудники, которые успешно проходят электронный курс, дольше работают в компании.
Про набор данных
Я использовал язык Python и библиотеки lifelines и patsy. На деле существует еще одна библиотека для анализа выживаемости scikit-survival, но с ней я еще не работал, хотя говорят, что она достаточно хороша.
Я пропущу описание процесса предварительной обработки данных. После всех манипуляций набор выглядел так:
<class 'pandas.core.frame.DataFrame'> Int64Index: 10902 entries, 2 to 11162 Data columns (total 13 columns): person_fullname 10902 non-null object person_department 10902 non-null object position_name 10902 non-null object hire_date 10902 non-null datetime64[ns] dismiss_date 10902 non-null datetime64[ns] course_start_date 10902 non-null datetime64[ns] course_end_date 10902 non-null datetime64[ns] score 10902 non-null float64 status 10902 non-null object sex 10902 non-null category is_dismiss 10902 non-null bool job_exp 10902 non-null int64 time_course 10902 non-null int64
В наборе 10902 наблюдения.
Попробуем оценить функцию выживаемости с помощью lifelines
duration= data_bts.job_exp
event = data_bts.is_dismiss
from lifelines import KaplanMeierFitter
kmf = KaplanMeierFitter()
kmf.fit(duration, event)
kmf.survival_function_.plot(xticks=np.arange(0, 5500, 200), yticks=np.arange(0,1.1, 0.1),
figsize=(15, 10), color='blue');
plt.show()
Вот что получилось:
event = data_bts.is_dismiss
from lifelines import KaplanMeierFitter
kmf = KaplanMeierFitter()
kmf.fit(duration, event)
kmf.survival_function_.plot(xticks=np.arange(0, 5500, 200), yticks=np.arange(0,1.1, 0.1),
figsize=(15, 10), color='blue');
plt.show()
Вот что получилось:
На графике видно, что рубеж в 175 дней преодолевает практически 80%, и 35% сотрудников преодолевает рубеж в 1,5 года. Медиана равна 466 дней.
Проверим, верно ли мы интерпретировали график:
print(kmf.median_) - 469
print(kmf.survival_function_.KM_estimate[175]) - 0.803675249511
print(kmf.survival_function_.KM_estimate[547]) - 0.350953224329
Построим функцию угрозы используя процедуру Нельсон-Аалена:
from lifelines import NelsonAalenFitter
naf = NelsonAalenFitter()
naf.fit(duration, event)
naf.plot(xticks=np.arange(0, 5500, 200), figsize=(15, 10), c='blue')
plt.show()
На графике видно, что после 400 дней риск увольнения резко возрастает.
А теперь сравним, тех кто прошел электронный курс и тех кто не прошел:
status = data_bts.status
mask = (data_bts.status == 'Пройден')
kmf.fit(duration[mask], event[mask], label='Пройден')
ax = kmf.plot(xticks=np.arange(0, 5500, 200), yticks=np.arange(0,1.1, 0.1),
figsize=(15, 10), c='g');
kmf.fit(duration[~mask], event[~mask], label='Не пройден')
kmf.plot(ax=ax, c='r')
plt.show()
Что же мы видим? Продолжительность жизни в компании у тех кто не прошел электронный курс выше, чем у тех кто прошел.
Не будем верить тому, что видим и проверим это с помощью лог ранк теста:
from lifelines.statistics import logrank_test
mask = (data_bts.status == 'Пройден')
results = logrank_test(duration[mask], duration[~mask], event[mask], event[~mask], alpha=.95 )
results.print_summary()
Results t 0: -1 test: logrank alpha: 0.95 null distribution: chi squared df: 1 __ p-value ___|__ test statistic __|____ test result ____|__ is significant __ 0.00000 | 124.084 | Reject Null | True
Судя по уровню значимости, который очень мал (для любопытных: 3.726650854557449e-29) мы можем отвергнуть нашу нулевую гипотезу. Получается, что те кто прошел электронный курс имеют больший шанс покинуть компанию, чем те кто не прошел.
Аддитивная модель Аалена
Все что было сделано выше является взглядом сверху. Однако, зачастую у нас есть и другие факторы, которые влияют на продолжительность жизни.
Построим нашу модель:
from lifelines import AalenAdditiveFitter
aaf = AalenAdditiveFitter(coef_penalizer=1.0, fit_intercept=True)
Импортируем библиотеку patsy для создания дизайн-матрицы факторов. К слову в scikit-learn аналогом этого являются dummie переменные.
import patsy
bts_factor_matrix = patsy.dmatrix('person_department + position_name + score + time_course -1', data_bts, return_type='dataframe')
Создадим новые признаки в нашей матрице
bts_factor_matrix['duration'] = data_bts.job_exp
bts_factor_matrix['event'] = data_bts.is_dismiss
Настроим нашу модель
aaf.fit(bts_factor_matrix, 'duration', 'event')
Чтобы оценка модели была более строгой, воспользуемся встроенной в lifelines процедурой кросс-валидации:
from lifelines.utils import k_fold_cross_validation
score = k_fold_cross_validation(aaf, bts_factor_matrix, 'duration', 'event', k=10)
print(np.mean(score))
print(np.std(score))
Точность нашей модели равна 0,66 со стандартным отклонением 0,04.
Вывод:
Можно сказать, что качество нашего алгоритма получилось не очень хорошим. Средняя точность по всем итерациям равна 0,66. Скорее всего необходимо сделать более качественный feature engineering и добавить в модель больше признаков. Очевидно, что в модель необходимо включать разные курсы и делить их по категориям.
Александр, а почему Pyton, а не R?
ОтветитьУдалитьЭдуард, мне Python удобнее и понятнее. Я попробую это переписать в R. Еще я подумал, что было бы не плохо разбавить посты с R.
УдалитьА диаграмму не пробовали справа обрезать? вам не кажется, что правый хвост не информативен?
УдалитьКажется. Действительно стоило обрезать.
Удалить