大数据使企业能够确定变量,预测自家公司的员工离职率。”——《哈佛商业评论》2017年8月
“员工流失分析就是评估公司员工流动率的过程,目的是预测未来的员工离职状况,减少员工流失情况。”——《福布斯》2016年3月
引言
本文我(作者Susan Li——译者注)会展示如何用Python库Scikit-Learn实现一个员工流动项目,文中我们会引入逻辑回归、随机森林、支持向量机等机器学习模型,也会衡量这些模型的准确率,并评估未来员工离职的情况。以上所有操作我们都是用Python来完成。开始吧!
数据预处理
我们本项目所用的数据来自Kaggle上的HR分析数据集(satisfaction_level (对公司满意程度,范围为0–1)
last_evaluation(从上一次评估以来的时间)
Number_projects(工作中完成的项目数量)
average_monthly_hours(平均每月工作时长)
Time_spend_company(在公司待了几年)
Work_accident(员工在工作期间是否出现过事故)
Left(员工是否离开了工作岗位(1或0))
Promotion_last_5years(过去5年员工是否升职过)
Sales(员工在哪个部门)
Salary(相对薪资水平)
import pandas as pd
hr = pd.read_csv('HR.csv')
col_names = hr.columns.tolist()
print("Column names:")
print(col_names)
print("\nSample data:")
hr.head()
列名:
[‘satisfaction_level’, ‘last_evaluation’, ‘number_project’, ‘average_montly_hours’, ‘time_spend_company’, ‘Work_accident’, ‘left’, ‘promotion_last_5years’, ‘sales’, ‘salary’]
样本数据:
重新将列名从“sales”命名为“department”
hr=hr.rename(columns = {'sales':'department'})
可以通过如下代码发现列的类型:
hr.dtypes
我们的数据相当干净,没有缺失值:
hr.isnull().any()
数据包含了14999名员工和10个特征:
hr.shape
(14999, 10)
“left”列为结果变量,值为1或0,1代表员工离开了公司,0代表没有离开。
数据集的“department”列有很多分类,我们需要减少分类以更好的建模。“department”列有如下分类:
hr['department'].unique()
数组:
([‘sales’, ‘accounting’, ‘hr’, ‘technical’, ‘support’, ‘management’,
‘IT’, ‘product_mng’, ‘marketing’, ‘RandD’], dtype=object)
我们将“technical”,“support”“IT”合并在一起,称为“technical”。
import numpy as np
hr['department']=np.where(hr['department'] =='support', 'technical', hr['department'])
hr['department']=np.where(hr['department'] =='IT', 'technical', hr['department'])
经过这番变动后,“department”分类会如下所示:
[‘sales’ ‘accounting’ ‘hr’ ‘technical’ ‘management’ ‘product_mng’
‘marketing’ ‘RandD’]
数据探索
首先,我们找到离开公司的员工数量以及没有离开的数量:
hr['left'].value_counts()
我们发现数据中有3571名员工离职,有11428名员工没有离职。
然后我们进一步查看这两个类别下的数字所蕴含的意义:
hr.groupby('left').mean()
几个观察结果:留在公司的员工的平均满意程度要高于离职的员工
留在公司的员工的平均每月工作时长要多于离职的员工
出现工作事故的员工比没有出现工作事故的员工离开公司的意愿要低
过去五年中得到升职的员工比过去五年中没有升职的员工离开公司的意愿要低
我们可以计算分类变量的分类平均值比如“工作部门”和“薪资”,更详细的理解我们的数据:
hr.groupby('department').mean()
hr.groupby('salary').mean()
数据可视化
我们将数据进行可视化,从而获取数据及重要特征的更清晰的展现。
员工的工作部分和离职频率的条形图
%matplotlib inline
import matplotlib.pyplot as plt
pd.crosstab(hr.department,hr.left).plot(kind='bar')
plt.title('Turnover Frequency for Department')
plt.xlabel('Department')
plt.ylabel('Frequency of Turnover')
plt.savefig('department_bar_chart')
很明显员工离职的频率和他们所在的部门有很大的关系。这样看来,部门就可以作为结果变量的一个很好的预测指标。
员工薪资水平和离职频率的条形图
table=pd.crosstab(hr.salary, hr.left)
table.div(table.sum(1).astype(float), axis=0).plot(kind='bar', stacked=True)
plt.title('Stacked Bar Chart of Salary Level vs Turnover')
plt.xlabel('Salary Level')
plt.ylabel('Proportion of Employees')
plt.savefig('salary_bar_chart')
我们可以看到,员工的薪资水平和离职频率有很大关系,因此薪资水平也可以作为预测结果的一个很好指标。
通常,直方图是我们在探索数据时用于分析数值变量的最有用的工具之一。
数值变量的直方图
num_bins = 10
hr.hist(bins=num_bins, figsize=(20,15))
plt.savefig("hr_histogram_plots")
plt.show()
为分类变量创建虚变量
数据集中有两个分类变量(department,salary),在用它们进行建模前需要将它们转换为虚变量。
cat_vars=['department','salary']
for var in cat_vars:
cat_list='var'+'_'+var
cat_list = pd.get_dummies(hr[var], prefix=var)
hr1=hr.join(cat_list)
hr=hr1
等创建完虚变量后,还需要移除实际的分类变量。
为分类变量创建虚变量后的列名:
hr.drop(hr.columns[[8, 9]], axis=1, inplace=True)
hr.columns.values
数组:([‘satisfaction_level’, ‘last_evaluation’, ‘number_project’,
‘average_montly_hours’, ‘time_spend_company’, ‘Work_accident’,
‘left’, ‘promotion_last_5years’, ‘department_RandD’,
‘department_accounting’, ‘department_hr’, ‘department_management’,
‘department_marketing’, ‘department_product_mng’,
‘department_sales’, ‘department_technical’, ‘salary_high’,
‘salary_low’, ‘salary_medium’], dtype=object)
结果变量为“left”,所有其它变量均为预测指标。
hr_vars=hr.columns.values.tolist()
y=['left']
X=[i for i in hr_vars if i not in y]
特征选择
递归特征消除(RFE)方法能够递归地移除变量,用剩余变量搭建模型。它能够用模型准确率来确定哪些变量(以及变量的合并)对预测目标属性的贡献最大。
我们使用特征选择方法帮我们决定哪些变量非常重要,能够以很高的准确率预测员工的流动状况。现在X中一共有18个列,那么选为10个呢?
from sklearn.feature_selection import RFE
from sklearn.linear_model import LogisticRegression
model = LogisticRegression()
rfe = RFE(model, 10)
rfe = rfe.fit(hr[X], hr[y])
print(rfe.support_)
print(rfe.ranking_)
[True True False False True True True True False True True False
False False False True True False]
[1 1 3 9 1 1 1 1 5 1 1 6 8 7 4 1 1 2]
你可以看到RFE为我们选择了10个变量,在support_array中标为True,在ranking_array中选为“1”。它们是:
[‘satisfaction_level’, ‘last_evaluation’, ‘time_spend_company’, ‘Work_accident’, ‘promotion_last_5years’, ‘department_RandD’, ‘department_hr’, ‘department_management’, ‘salary_high’, ‘salary_low’]
cols=['satisfaction_level', 'last_evaluation', 'time_spend_company', 'Work_accident', 'promotion_last_5years',
'department_RandD', 'department_hr', 'department_management', 'salary_high', 'salary_low']
X=hr[cols]
y=hr['left']
逻辑回归模型
from sklearn.cross_validation import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
from sklearn.linear_model import LogisticRegression
from sklearn import metrics
logreg = LogisticRegression()
logreg.fit(X_train, y_train)
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=100, multi_class=’ovr’, n_jobs=1, penalty=’l2', random_state=None, solver=’liblinear’, tol=0.0001, verbose=0, warm_start=False)
from sklearn.metrics import accuracy_score
print('Logistic regression accuracy: {:.3f}'.format(accuracy_score(y_test, logreg.predict(X_test))))
逻辑回归准确率:0.771
随机森林
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier()
rf.fit(X_train, y_train)
RandomForestClassifier(bootstrap=True, class_weight=None, criterion=’gini’, max_depth=None, max_features=’auto’, max_leaf_nodes=None, min_impurity_split=1e-07, min_samples_leaf=1, min_samples_split=2, min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1, oob_score=False, random_state=None, verbose=0, warm_start=False)
print('Random Forest Accuracy: {:.3f}'.format(accuracy_score(y_test, rf.predict(X_test))))
随机森林准确率:0.978
支持向量机
from sklearn.svm import SVC
svc = SVC()
svc.fit(X_train, y_train)
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
decision_function_shape=None, degree=3, gamma=’auto’, kernel=’rbf’,
max_iter=-1, probability=False, random_state=None, shrinking=True,
tol=0.001, verbose=False)
print('Support vector machine accuracy: {:.3f}'.format(accuracy_score(y_test, svc.predict(X_test))))
支持向量机准确率:0.909
通过对比这三个模型,我们可以看到随机森林的准确率最高。
交叉验证
交叉验证的目的是在为每个观测数据集生成预测值的同时,避免出现过拟合。我们现在使用10折交叉验证法来训练我们的随机森林模型。
from sklearn import model_selection
from sklearn.model_selection import cross_val_score
kfold = model_selection.KFold(n_splits=10, random_state=7)
modelCV = RandomForestClassifier()
scoring = 'accuracy'
results = model_selection.cross_val_score(modelCV, X_train, y_train, cv=kfold, scoring=scoring)
print("10-fold cross validation average accuracy: %.3f" % (results.mean()))
10折交叉验证平均准确率:0.977
平均准确率仍然和随机森林模型的准确率非常接近,所以我们可以总结出模型的泛化能力很好。
精确度和召回率
我们构建混淆矩阵来可视化由分类器生成的预测值,并评估分类的准确率。
随机森林
from sklearn.metrics import classification_report
print(classification_report(y_test, rf.predict(X_test)))
y_pred = rf.predict(X_test)
from sklearn.metrics import confusion_matrix
import seaborn as sns
forest_cm = metrics.confusion_matrix(y_pred, y_test, [1,0])
sns.heatmap(forest_cm, annot=True, fmt='.2f',xticklabels = ["Left", "Stayed"] , yticklabels = ["Left", "Stayed"] )
plt.ylabel('True class')
plt.xlabel('Predicted class')
plt.title('Random Forest')
plt.savefig('random_forest')
逻辑回归
print(classification_report(y_test, logreg.predict(X_test)))
logreg_y_pred = logreg.predict(X_test)
logreg_cm = metrics.confusion_matrix(logreg_y_pred, y_test, [1,0])
sns.heatmap(logreg_cm, annot=True, fmt='.2f',xticklabels = ["Left", "Stayed"] , yticklabels = ["Left", "Stayed"] )
plt.ylabel('True class')
plt.xlabel('Predicted class')
plt.title('Logistic Regression')
plt.savefig('logistic_regression')
支持向量机
print(classification_report(y_test, svc.predict(X_test)))
svc_y_pred = svc.predict(X_test)
svc_cm = metrics.confusion_matrix(svc_y_pred, y_test, [1,0])
sns.heatmap(svc_cm, annot=True, fmt='.2f',xticklabels = ["Left", "Stayed"] , yticklabels = ["Left", "Stayed"] )
plt.ylabel('True class')
plt.xlabel('Predicted class')
plt.title('Support Vector Machine')
plt.savefig('support_vector_machine')
当一名员工离职时,我们的分类器能正确预测的概率是多少呢?这种衡量方法被称为“召回率”,快速看一下这些图表可以发现随机森林的效果最好。在上面所有的离职情况中,随机森林从1038种离职情况中正确地检索出998种情况。这表明离职“召回率”达到95%(991/1038),远好于逻辑回归(26%)和支持向量机(85%)。
当一个分类器预测一名员工即将离职时,员工真的会离职的概率怎样?这种衡量方法被称为“精确度”。随机森林再次好于其它两种模型:随机森林的精确度达到95%(991/1045),而逻辑回归为51%(273/540),支持向量机为77%(890/1150)。
ROC 曲线
from sklearn.metrics import roc_auc_score
from sklearn.metrics import roc_curve
logit_roc_auc = roc_auc_score(y_test, logreg.predict(X_test))
fpr, tpr, thresholds = roc_curve(y_test, logreg.predict_proba(X_test)[:,1])
rf_roc_auc = roc_auc_score(y_test, rf.predict(X_test))
rf_fpr, rf_tpr, rf_thresholds = roc_curve(y_test, rf.predict_proba(X_test)[:,1])
plt.figure()
plt.plot(fpr, tpr, label='Logistic Regression (area = %0.2f)' % logit_roc_auc)
plt.plot(rf_fpr, rf_tpr, label='Random Forest (area = %0.2f)' % rf_roc_auc)
plt.plot([0, 1], [0, 1],'r--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver operating characteristic')
plt.legend(loc="lower right")
plt.savefig('ROC')
plt.show()
ROC曲线是另一种用于衡量二元分类器的常用工具。它是一条点状线,表示一个完全随机分类器的受试者工作特征曲线;一个性能良好的分类器会尽可能的远离这条线(朝左上角)。
随机森林模型的特征重要程度
feature_labels = np.array(['satisfaction_level', 'last_evaluation', 'time_spend_company', 'Work_accident', 'promotion_last_5years',
'department_RandD', 'department_hr', 'department_management', 'salary_high', 'salary_low'])
importance = rf.feature_importances_
feature_indexes_by_importance = importance.argsort()
for index in feature_indexes_by_importance:
print('{}-{:.2f}%'.format(feature_labels[index], (importance[index] *100.0)))
promotion_last_5years-0.20%
department_management-0.22%
department_hr-0.29%
department_RandD-0.34%
salary_high-0.55%
salary_low-1.35%
Work_accident-1.46%
last_evaluation-19.19%
time_spend_company-25.73%
satisfaction_level-50.65%
最后我们的随机森林模型为我们显示出影响员工是否会离职的最重要的特征,其中影响最大的特征为:满意程度,在公司入职时长,距离上一次工作评估时长,工作事故和薪水等。
我们依靠这些特征就能预测未来的员工离职概率。
总结
文章到这里就结束了,本文我们通过一个人力资源数据集,用Python搭建了几个机器学习模型,最终确定随机森林模型的准确率最高,最适合预测员工离职。
不过我就不列出模型预测哪些员工会离职了,毕竟这并非我们的主要目的。记住一点,我们的算法不会人人适用。虽然员工离职率分析能帮我们制定更合理的人力资源管理决策,但不要过度依赖模型的预测结果。