Análisis Abandono de Empleados

Aug 21, 2025

Proyecto de Business Intelligence para predecir el abandono de empleados, medir su impacto económico y proponer estrategias de retención.

Repositorios:



El gran objetivo del proyecto en el que vas a trabajar es reducir el abandono de empleados de la empresa. El principal problema es la rotación de empleados. Deberás trabajar en un sistema que consiga reducir la fuga de empleados.

Objetivos

  1. Entender y cuantificar el problema desde el punto de vista de negocio
  2. Desarrollar un sistema automatizado de machine learning que identifique a los empleados que están en mayor riesgo de fuga
  3. Comunicar los resultados de forma exitosa a la dirección

Fases

  • Business Analytics: entender y cuantificar el problema
  • Machine Learning: desarrollar un modelo predictivo
  • Visualización y Comunicación: generar un cuadro de mando

Business Analytics

Carga de librerías y dataset

import numpy as np
import pandas as pd 
import matplotlib.pyplot as plt
import os

ruta = os.path.join(os.getcwd())
df = pd.read_csv(ruta + r'/data/AbandonoEmpleados.csv', sep=';', na_values='#N/D')
df.head()
df.info()

Análisis de nulos

df.isnull().sum().sort_values(ascending=False)

Conclusiones:

  • Eliminamos columnas con muchos nulos: años_en_puesto y conciliacion.
  • Imputamos valores en sexo, educación, satisfacción e implicación.
df.drop(columns = ['anos_en_puesto','conciliacion'], inplace = True)
df['educacion'] = df['educacion'].fillna('Universitaria')
df['implicacion'] = df['implicacion'].fillna('Alta')
df['satisfaccion_trabajo'] = df['satisfaccion_trabajo'].fillna('Alta')

Variables numéricas

Conclusiones:

  • Eliminamos sexo, empleados, horas_quincena.
df.drop(columns=['sexo','empleados','horas_quincena'], inplace=True)

Generación de insights

Tasa de abandono

df['abandono'].value_counts(normalize=True)*100

Impacto económico

df['salario_ano'] = df['salario_mes'] * 12

condiciones=[
    (df['salario_ano']<=30000),
    (df['salario_ano']>30000) & (df['salario_ano']<=50000),
    (df['salario_ano']>50000) & (df['salario_ano']<=75000),
    (df['salario_ano']>75000)
]
ponderaciones=[df['salario_ano']*0.161, df['salario_ano']*0.197, df['salario_ano']*0.204, df['salario_ano']*0.21]

df['impacto_abandono'] = np.select(condiciones, ponderaciones, default=-999)
coste_total = df.loc[df['abandono']=='Yes'].impacto_abandono.sum()

Machine Learning

Preparación de datos

df['abandono'] = df.abandono.map({'No':0, 'Yes':1})
from sklearn.preprocessing import OneHotEncoder

df_ml = df.copy()
cat = df_ml.select_dtypes('O')
ohe = OneHotEncoder(sparse_output=False)
ohe.fit(cat)
cat_ohe = pd.DataFrame(ohe.transform(cat), columns=ohe.get_feature_names_out(cat.columns)).reset_index(drop=True)

num = df_ml.select_dtypes('number').reset_index(drop=True)
df_ml = pd.concat([cat_ohe, num], axis=1)

Modelos candidatos

from sklearn.model_selection import train_test_split, StratifiedKFold, cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC

X = df_ml.drop(columns='abandono')
y = df_ml['abandono']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

models = [
    ('LR', LogisticRegression(solver='liblinear', multi_class='ovr')),
    ('KNN', KNeighborsClassifier()),
    ('RF', RandomForestClassifier()),
    ('SVM', SVC(gamma='auto'))
]

for name, model in models:
    kfold = StratifiedKFold(n_splits=10, random_state=1, shuffle=True)
    cv_results = cross_val_score(model, X_train, y_train, cv=kfold, scoring='accuracy')
    print(f"{name}: {cv_results.mean():.4f} ({cv_results.std():.4f})")

RF optimizado: Mejores parámetros

from sklearn.model_selection import RandomizedSearchCV

param_dist = {
    'n_estimators': [50, 100, 200, 300],
    'max_depth': [None, 10, 20, 30],
    'min_samples_split': [2, 5, 10],
    'min_samples_leaf': [1, 2, 4],
    'max_features': ['sqrt', 'log2']
}

random_search = RandomizedSearchCV(
    estimator=RandomForestClassifier(), 
    param_distributions=param_dist,
    n_iter=50, cv=5, scoring='f1_macro', verbose=2, n_jobs=-1, random_state=42
)

random_search.fit(X_train, y_train)
print(random_search.best_params_)

Evaluación RF optimizado

RF = RandomForestClassifier(**random_search.best_params_)
RF.fit(X_train, y_train)
RF_pred = RF.predict(X_test)

from sklearn.metrics import accuracy_score, confusion_matrix, classification_report

print(accuracy_score(y_test, RF_pred))
print(confusion_matrix(y_test, RF_pred))
print(classification_report(y_test, RF_pred))

Scoring de abandono

df['scoring_abandono'] = RF.predict_proba(df_ml.drop(columns='abandono'))[:,1]
df.sort_values(by='scoring_abandono', ascending=False).head(10)



Visualización y Comunicación