Estudo de Caso RH – Reduzir atritos sistemáticos dos colaboradores e fazer os talentos permanecerem (Parte 2: Predição)

Contexto

Na primeira parte de nossa análise, nós juntamos alguns insights básicos sobre como nosso conjunto de dados e vimos que nossas features apresentaram altas taxas de correlação.

Nesta parte, nós usamos os dados que nós preparamos para predizer quais colaboradores possuem tendência a deixar a organização.

Se você não é um expert da tecnologia, você pode ler o o resumo que preparamos abaixo e você pode voltar a trabalhar 😉

Resumo para não-experts em tecnologia (e todos aqueles que querem gastar 1 minuto para entender o que fizemos):

  • Nós usamos um conjunto de dados típico de um departamento de RH como base. Esta base contém colunas que podem ser facilmente encontradas na maioria dos sistemas TI dos departamentos de RH (anos na organização, última avaliação, nível salarial, última promoção, acidentes de trabalho, nível de satisfação, etc.). Você pode checar o arquivo aqui.
  • Nós criamos um modelo capaz de prever com uma extrema precisão quais colaboradores vão deixar a organização e quais permanecerão. O nível de precisão do modelo é de 98,8%, ou seja, somente em 1,2% dos casos o modelo não detecta corretamente se o colaborador deixará a empresa.
  • Nós usamos a tecnologia preditiva para calcular a probabilidade exata de cada colaborador deixar a empresa.
  • Finalmente, nós geramos uma lista dos colaboradores-chave (colaboradores com uma taxa de avaliação > 0,7) ordenado por probabilidade de deixar a organização. Você pode ver o output final aqui.

 

 

Etapas detalhadas

Nós utilizamos o módulo “Files” do Verteego Data Science Suite para produzir alguns scripts de data science bastante diretos (com Python e Scikit Learn), de maneira a predizer quais eram os colaboradores-chave (aqueles com avaliações superiores a 0,7) mais propensos a sair.

Você pode executar ou baixar o notebook aqui.

Nós veremos na terceira e última etapa deste Estudo de Caso como o Verteego Data Science Suite pode ser utilizado para informar os gestores dos empregados sobre a situação dentro de seus times, através de um mailing mensal, por exemplo.

Abaixo, você encontrará uma passagem do script. Caso queira executar a análise por conta própria, você pode baixar todos os arquivos do repositório aqui.

 

Importando as principais bibliotecas

Nesta análise, usamos Pandas, Numpy e Scikit.

import pandas as pd
import numpy as np

#disable warnings to make notebook smoother
import warnings
warnings.filterwarnings('ignore')

 

Carregando os dados

Na primeira parte deste Estudo de Caso, nós limpamos os dados brutos e mudamos alguns nomes de colunas para estabelecer um padrão.
Assim, nós carregamos 15 mil linhas de dados dentro do quadro de dados, com os nomes das colunas exibidos assim como as cinco primeiras linhas do conjunto de dados.

# load data frame
leave_df = pd.read_csv('../data/raw_data.csv')
col_names = leave_df.columns.tolist()

#show some basic output
print "Column names:"
print col_names

print "\nSample data:"
leave_df.head(6)

Output:

screenshot-from-2016-12-06-18-20-53

screenshot-from-2016-12-06-18-21-30

 

Preparando as features

A parte importante desta etapa – e talvez de todo o script da predição – é a feature de padronização. Depois de harmonizar os identificadores dos departamentos e colocar todos em uma matriz, o feature de padronização (usando StandardScaler class) converte todos os valores em uma escala de -1.0 a 1.0. Este simples procedimento contribui consideravelmente para a precisão das previsões.

# Isolate target data
y = leave_df['left']

# We don't need these columns
to_drop = ['name', 'salary', 'left']
leave_feat_space = leave_df.drop(to_drop,axis=1)

# Pull out features for future use
features = leave_feat_space.columns

# convert label features to integers
from sklearn import preprocessing
le_sales = preprocessing.LabelEncoder()
le_sales.fit(leave_feat_space["department"])
leave_feat_space["department"] = le_sales.transform(leave_feat_space.loc[:,('department')])

# transforme the whole feature space into a matrix
X = leave_feat_space.as_matrix().astype(np.float)

# standardize all features
scaler = preprocessing.StandardScaler()
X = scaler.fit_transform(X)

print "Feature space holds %d observations and %d features" % X.shape
print "Unique target labels:", np.unique(y)

Output:

screenshot-from-2016-12-06-21-28-48

 

Função de predição

A função de predição utiliza utiliza uma validação cruzada (3 vezes) e é escrita considerando vários algoritmos de predição como input (nós usaremos 3 algoritmos diferentes no próximo passo). Nós utilizamos esta função tanto para comparar os algoritmos quanto a predizer as probabilidades dos colaborradores deixar a organização na última etapa do nosso script (através do parâmetro “method”).

# prediction function
def run_cv(X,y,clf_class, method, **kwargs):
    
	  from sklearn.model_selection import cross_val_predict

	  # Initialize a classifier with key word arguments
	  clf = clf_class(**kwargs)

	  predicted = cross_val_predict(clf, X, y, cv=3, method=method)

	  return predicted

 

Comparar algoritmos preditivos

De maneira a melhor classificar o algoritmo, nós comparamos o Support Vector Classifier (SVC), a Random Forest Classifier (RF) e a K-Nearest-Neighbors Classifier (KNN). Logo que tivemos visualizamos o primeiro output, todos os modelos produzidos apresentaram resultados satisfatórios (com precisões entre 95% e 99%) sem mesmo realizar um tuning nos algoritmos. Finalmente, nós entendemos que a Random Forest Classifier para a predição uma vez que esta nos traz os melhores resultados (precisão de 98,8%).

from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier as RF
from sklearn.neighbors import KNeighborsClassifier as KNN
from sklearn import metrics

def accuracy(y, predicted):
    # NumPy interprets True and False as 1. and 0.
    return metrics.accuracy_score(y, predicted)

print "Support vector machines:"
print "%.3f" % accuracy(y, run_cv(X,y,SVC, method='predict'))
print "Random forest:"
print "%.3f" % accuracy(y, run_cv(X,y,RF, method='predict'))
print "K-nearest-neighbors:"
print "%.3f" % accuracy(y, run_cv(X,y,KNN, method='predict'))

Output:

screenshot-from-2016-12-06-21-41-38

 

Calculando a matriz de confusão

As matrizes de confusão são ferramentas úteis para ter uma visão geral da precisão da predição. A matriz fornece um valor para cada ponto previsto e realizado. Em nosso exemplo, há 4 campos: Left-Left, Left-Not left, Not left-Left and Not left-Not left. Graças a alta qualidade da precisão os campos Left-Left e Not left-Not left representam a maior parte dos valores.

from sklearn.metrics import confusion_matrix

y = np.array(y)
class_names = np.unique(y)

# calculate confusion matrices
confusion_matrices = [
	  ( "Support Vector Machines", confusion_matrix(y,run_cv(X,y,SVC, method='predict')) ),
	  ( "Random Forest", confusion_matrix(y,run_cv(X,y,RF, method='predict')) ),
	  ( "K-Nearest-Neighbors", confusion_matrix(y,run_cv(X,y,KNN, method='predict')) ),
]

# show confusion matrix values
print confusion_matrices

Output:

screenshot-from-2016-12-06-21-56-37

 

Desenhando as matrizes de confusão

Nós usamos a biblioteca de visualização Seaborn para desenhar a matriz de confusão para nossos 3 algoritmos de previsão.

Por exemplo, podemos ver o modelo do Random Forest Classifier com a previsão correta em 11.375 vezes que o colaborador permaneceria na empresa.

import matplotlib.pyplot as plt
import seaborn as sn
%matplotlib inline

# draw confusion matrices
for cf in confusion_matrices:

	  ax = plt.axes()
	  ax.set_title(cf[0])

	  df_cm = pd.DataFrame(cf[1], index = ["Real 0", "Real 1"], columns = ["Pred 0", "Pred 1"])
	  plt.figure(figsize = (6,5))
	  sn.heatmap(df_cm, annot=True, ax = ax, square=True, fmt="d",linewidths=.5)

Output:

svmrfknn

 

Calculando as probabilidades da previsão para todos os colaboradores

Nós usamos a função de predição para calcular as probabilidades de permanência e de saída (left=0, left=1) para os 15 mil colaboradores do nosso conjunto de dados. Como todo modelo de previsão, fizemos centenas ou mesmo milhares de previsões, nós podemos comparar as probabilidades de cada uma das classes.

Por exemplo, para o grupo de colaboradores que possuía a probabilidade de 60% deixar a organização, a real probabilidade de saída era de 78%. Como podemos visualizar, as probabilidades previstas para as duas classes (pred_prob=0% e pred_prob=100%) são muito próximos das reais probabilidade que comprovam a precisão do nosso modelo.

# Use 10 estimators so predictions are all multiples of 0.1
pred_prob = run_cv(X, y, RF, n_estimators=10,  method='predict_proba',)

pred_leave = pred_prob[:,1]
is_leave = y == 1

# Number of times a predicted probability is assigned to an observation
counts = pd.value_counts(pred_leave)

# calculate true probabilities
true_prob = {}
for prob in counts.index:
	  true_prob[prob] = np.mean(is_leave[pred_leave == prob])
	  true_prob = pd.Series(true_prob)

# pandas-fu
counts = pd.concat([counts,true_prob], axis=1).reset_index()
counts.columns = ['pred_prob', 'count', 'true_prob']
counts

Output:

screenshot-from-2016-12-06-22-19-12

 

Gerando a lista de colaboradores com probabilidades de ficar e de deixar a organização

Na última etapa, nós filtramos a partir de todos os coolaboradores (colaboradores que tiveram uma avaliação maior que 0,7) que ainda estão na organização. Isto nos reduz a 6 mil colaboradores. De maneira a alertar os gestores sobre os colaboradores, um caminho seria disponibilizar uma lista com quais colaboradores tem maior probabilidade de sair da organização e salvar toda a lista em arquivo CSV.

Você pode ver e baixar o output final aqui.

#create a dataframe containing prob values
pred_prob_df = pd.DataFrame(pred_prob)
pred_prob_df.columns = ['prob_not_leaving', 'prob_leaving']

#merge dataframes to get the name of employees
all_employees_pred_prob_df = pd.concat([leave_df, pred_prob_df], axis=1)

#filter out employees still in the company and having a good evaluation
good_employees_still_working_df = all_employees_pred_prob_df[(all_employees_pred_prob_df["left"] == 0) & 
                                                            (all_employees_pred_prob_df["last_evaluation"] >= 0.7)]

good_employees_still_working_df.sort_values(by='prob_leaving', ascending=False, inplace=True)

#write to csv
good_employees_still_working_df.to_csv("/home/demos/hr-analytics/output/good_employees_leaving_prob.csv")