.

Сделать репост в соц сети!

понедельник, 29 января 2018 г.

Как прохождение электронного курса влияет на выживаемость персонала



Как прохождение электронного курса влияет на выживаемость персонала


Зачем это все?


Приветствую, коллеги. В данном посте я хочу рассказать вам о том, как я решал (и еще решаю) задачу анализа выживаемости персонала в зависимости от того, пройден или нет электронный курс. Главный гость нашего набора данных - электронный вводный курс, который назначается 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()

Вот что получилось:


На графике видно, что рубеж в 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 и добавить в модель больше признаков. Очевидно, что в модель необходимо включать разные курсы и делить их по категориям.





4 комментария:

  1. Александр, а почему Pyton, а не R?

    ОтветитьУдалить
    Ответы
    1. Эдуард, мне Python удобнее и понятнее. Я попробую это переписать в R. Еще я подумал, что было бы не плохо разбавить посты с R.

      Удалить
    2. А диаграмму не пробовали справа обрезать? вам не кажется, что правый хвост не информативен?

      Удалить
    3. Кажется. Действительно стоило обрезать.

      Удалить