任务目标:
- 辞职原因相关性分析
- 建立机器学习模型预测员工是否离职
Part one、描述性分析
1. 导入相关的包
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import sqlalchemy
%matplotlib inline
2. 导入数据
数据源于 Datafrog.
#连接mysql数据库
import pymysql
conn = pymysql.connect(host='数据库地址', user='用户名', password='密码', database='数据库名', charset="utf8")
#获取数据
df = pd.read_sql("select * from 表名", conn)
df.head()
3. 变量说明及变量情况
3.1 缺失情况
# any 的作用是看每一列是否有一个为真则为真。而如果有一个为真,那说明是有缺失值的
df.isnull().any()
3.2 数据形状
一共10个变量,其中1个因变量,9个自变量。14999条记录。
df.shape
(14999, 10)
3.3变量类型
satisfaction_level(对公司的满意程度)、last_evaluation(绩效评估)是浮点数,取值为0-1之间;
number_project(参加过的项目数)、average_montly_hours(平均每月工作时长)、time_spend_company(工作年限)、Work_accident(工作是否出过差错)、promotion_last_5years(5年内是否升职)是整数类型;
sales(职业)、salary(工资)是对象类型
left(是否离职)是整数类型,0代表未离职,1代表离职。
df.dtypes
# 给字段取别名
df = df.rename(columns={'satisfaction_level': '满意程度',
'last_evaluation': '绩效评估',
'number_project': '项目数',
'average_montly_hours': '每月平均工作时长',
'time_spend_company': '工作年限',
'Work_accident': '工作是否出过差错',
'promotion_last_5years': '5年内是否升职',
'sales' : '职业',
'salary' : '薪资水平',
'left' : '是否离职'
})
3.4 数据分布
# 数据整体分布
df.describe()
可以看出:
**61.28%**的员工对公司是满意的;
员工的平均绩效是0.71;
跟进项目最多的是7个,最少的是2个,平均为3.8个
平均月工作时长标准差为49.94,说明员工间工作时长差别较大;
工作年限为2年到10年,平均3.5年;
只有**14.46%**的人工作中出过差错;
5年内升职的员工只有2.13%。
查看职业和薪资中的类别的具体值
- 职业类别有10类,分别为’sales’ ‘accounting’ ‘hr’ ‘technical’ ‘support’ ‘management’ ‘IT’ ‘product_mng’ ‘marketing’ ‘RandD’。
- 薪资水平分为’low’ ‘medium’ 'high’三类。
print(pd.Series(df['职业']).unique(),pd.Series(df['薪资水平']).unique(),sep='\n')
由于“职业”和“薪资水平”的分类比较少,我们将把它转换为数值,以便更好地分析。
用0-10分别代替十种职业,
0-2代替薪资水平。
df['职业'].replace(list(pd.Series(df['职业']).unique()),np.arange(10),inplace=True)
df['薪资水平'].replace(list(pd.Series(df['薪资水平']).unique()),[0,1,2],inplace=True)
3.4.2 因变量分布
已离职占比23.81%,未离职占比76.19%。
# 计算离职与未离职和已离职人数占比。
left_rate=df.是否离职.value_counts(normalize=True)
left_rate
# 为了更直观的区分自变量和因变量,我们将因变量放到最前。
front=df['是否离职']
df.drop(labels='是否离职',axis=1,inplace=True)
df.insert(0,'是否离职',front)
df.head()
4. 探索分析
如何绘制热力图
[seaborn.heatmap-0.10.1]
http://seaborn.pydata.org/generated/seaborn.heatmap.html
- 重要参数释义:
- data:可以强制为ndarray的2D数据集。 如果提供了Pandas DataFrame,则索引/列信息将用于标记列和行。
- vmin, vmax:图例中最大值和最小值的显示值,没有该参数时默认不显示
- cmap:matplotlib的colormap名称或颜色对象;如果没有提供,默认为cubehelix map (数据集为连续数据集时) 或 RdBu_r (数据集为离散数据集时)
- center:将数据设置为图例中的均值数据,即图例中心的数据值;通过设置center值,可以调整生成的图像颜色的整体深浅;
- robust:如果为True且没有vmin或vmax,则使用健壮的分位数计算colormap范围,而不是使用极端值。
- xticklabels, yticklabels:如果是True,则绘制dataframe的列名。如果是False,则不绘制列名。如果是列表,则绘制列表中的内容作为xticklabels。 如果是整数n,则绘制列名,但每个n绘制一个label。 默认为True。
- annotate的缩写,annot默认为False,当annot为True时,在heatmap中每个方格写入数据
- fmt,格式设置。’.0%’#显示百分比; fmt =‘f’ 显示完整数字 = fmt =‘g’; fmt =’.3’显示小数的位数 = fmt =’.3f’ = fmt =’.3g’
4.1 变量间相关性分析
from pylab import *
mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False
# corr 可以计算各个列之间的相关系数
corr = df.corr()
f, ax = plt.subplots(figsize=(8,5))
sns.heatmap(corr,linewidths=.5,cmap="YlGnBu",annot=True,fmt ='0.1g')
plt.title('Heatmap of Correlation Matrix')
从热图上看:
- 有正(+)相关性的有,完成项目数(number_project)和平均每月工作时长(average_montly_hours),绩效评估,这三张两两之间有较大的正相关。
- 但是,绩效评估与升职加薪水平之间没有相关关系,也就是说绩效评估的高度评价没有转换到薪资水平和升职上来,只是得到了好的评价而已。
- 对于负(-)关系,离职率和薪水、离职率和满意度是高度相关的。
结果分析:
1、绩效评估、项目数、每月工作时长和升职加薪与否似乎关系不大。
2、对是否离职影响最大的是对公司的满意程度,而影响满意程度最大的因素
- 第一是绩效评估,绩效评估又和项目数成负相关,项目数越多越忙碌,满意度越低;
- 第二是工作时间越长满意度越低;
4.2 变量间关系
4.2.1 满意程度与是否离职
未离职的满意程度在0.7左右,已离职的满意度在0.4左右。
ax = sns.boxplot(x = '是否离职', y = '满意程度', data = df, width=0.45, palette="Blues", linewidth=3)
4.2.2薪资水平与是否离职
薪资水平是影响员工离职的最重要因素。薪资越高离职率越低,离职员工中,薪资水平低的人占多数,薪资水平高的人几乎没有。
depart_salary_table=pd.crosstab(index=df['是否离职'],columns=df['薪资水平'])
depart_salary_table.plot(kind="bar",figsize=(5,5),stacked=True, width=0.3,)
4.2.3 绩效与是否离职
离职员工的绩效评估表现明显好于未离职员工,这说明优秀的员工可能没有在职位与薪资得到激励。
ax = sns.boxplot(x = '是否离职', y = '绩效评估', data = df, width=0.45, palette="BuGn_r", linewidth=3)
4.2.4工作中是否出错与是否离职
工作是否出过差错可以从侧面反映一位员工的能力,离职员工犯错概率低于未离职员工的犯错几率。
depart_salary_table=pd.crosstab(index=df['是否离职'],columns=df['工作是否出过差错'])
depart_salary_table.plot(kind="bar",figsize=(5,5),stacked=True, width=0.3,)
4.2.6每月平均工作时长与是否离职
hours_left_table=pd.crosstab(index=df['每月平均工作时长'],columns=df['是否离职'])
fig=plt.figure(figsize=(8,5))
letf=sns.kdeplot(df.loc[(df['是否离职']==0),'每月平均工作时长'],color='b',shade=True,label='未离职')
left=sns.kdeplot(df.loc[(df['是否离职']==1),'每月平均工作时长'],color='r',shade=True,label='已离职')
这是一个很明显的双峰分布,说明员工平均每月工作时间少的(150小时左右)和工作时间多的(250小时左右)的员工离职率最高。
所以一般离开公司的员工要么工作时间少的,要么过度工作的。
4.2.7离职员工的满意度与绩效评估
from pandas.plotting import scatter_matrix
df1=df[df['是否离职']==1]
fig, ax = plt.subplots(figsize=(10,10))
scatter_matrix(df1[['满意程度','绩效评估']],color='r',ax=ax)
# plt.savefig('scatter.png',dpi=1000,bbox_inches='tight')
对于已离职的员工,在绩效评估与满意度的散射矩阵中,可以划分为3个不同的群体,他们分别代表了3种不同类型的员工。
集群1——优秀但对公司没有归属感:满意度低于0.2,绩效评估大于0.75,他们的绩效水平高,但是这可能代表他们工作压力大,工作时间长。
集群2——不优秀且对公司没有归属感:满意度在0.35~0.45之间,绩效评估在0.58以下,能力一般,且对公司归属感不强,他们的离职对于公司来说损失不大。
集群3——优秀且对公司没有归属感:满意度在0.7~1之间,评价大于0.8,这可能意味着这个集群的员工是最理想的,他们热爱他们的工作,公司对他们的表现评价很高,这个类别的员工离开可能是因为他们找到了另一个工作机会。
对于一家公司来说,如何针对集群1和集群3的员工提升他们额满意程度,降低他们的离职率尤为重要。
Part two、建模预测
1.特征工程
数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已。
1.1离散数据预处理
离散数据分两种,一种是定序,一种是定类。区别在于是否有顺序的区别。
- a.定序
薪资水平其含有顺序意义, 因此可将其字符型转化为数值型。
- b.定类
岗位是定类型变量, 对其进行one-hot编码, 这里直接利用pandas的get_dummies方法。
one-hot编码:
-
为什么需要one-hot编码?或者说类别数据有什么问题?
我们首先需要有一个认知:有些算法可以直接应用于类别数据,但还有许多机器学习算法并不能直接操作标签数据。这些算法要求所有的输入输出变量都是数值。通常来说,这种限制主要是因为这些机器学习算法的高效实现造成的,而不是算法本身的限制。
one hot编码是将类别变量转换为机器学习算法易于利用的一种形式的过程。 -
如何将类别数据转换成数值数据?
这包含两个步骤:
- 整数编码。先要给每个类别值都分配一个整数值。
- One-Hot 编码。One-Hot 编码会去除整数编码,并为每个整数值都创建一个二值变量。
-
如何编程实现?
# 前面已经对薪资水平“低中高”转化为数值型0-2。对岗位进行one-hot编码。
df = df.drop(['职业'], axis=1).join(pd.get_dummies(df['职业'], prefix="职业"))
df.shape
(14999, 19)
1.2连续型变量预处理
由于变量间有不同的量纲,所以需要归一化处理。
average_montly_hours = df['每月平均工作时长']
df['每月平均工作时长'] = df['每月平均工作时长'].apply(lambda x: (x-average_montly_hours.min()) / (average_montly_hours.max()-average_montly_hours.min()))
2.建模——逻辑回归
from sklearn.model_selection import train_test_split
#划分训练集和测试集
X = df.drop(['是否离职'], axis=1)
y = df['是否离职']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1)
2.1模型训练
from sklearn.linear_model import LogisticRegression
LR = LogisticRegression()
print(LR.fit(X_train, y_train))
print("训练集准确率: ", LR.score(X_train, y_train))
print("测试集准确率: ", LR.score(X_test, y_test))
2.2 调参
参数解读
- Cs:正则化系数,正则化强度的导数,必须是一个正数,值越小,正则化强度越大,即防止过拟合的程度更大。
- penalty:正则化选择参数,参数可选值为l1和l2,分别对应l1正则化和l2正则化,默认是l2正则化。
- solver:用来指明损失函数的优化方法,默认是‘liblinear’方法
- max_iter:算法收敛的最大迭代次数,即求取损失函数最小值的迭代次数,默认是100,
- scoring:准确度评价标准,默认None,
交叉验证
-
定义:
在给定的建模样本中,拿出大部分样本进行建模型,留小部分样本用刚建立的模型进行预报,并求这小部分样本的预报误差,记录它们的平方加和。这个过程一直进行,直到所有的样本都被预报了一次而且仅被预报一次。把每个样本的预报误差平方加和,称为PRESS(predicted Error Sum of Squares)。
-
目的:
用交叉验证的目的是为了得到可靠稳定的模型。
在建立PCR 或PLS 模型时,一个很重要的因素是取多少个主成分的问题。用cross validation 校验每个主成分下的PRESS值,选择PRESS值小的主成分数。或PRESS值不再变小时的主成分数。
常用的精度测试方法主要是交叉验证,例如10折交叉验证(10-fold cross validation),将数据集分成十份,轮流将其中9份做训练1份做验证,10次的结果的均值作为对算法精度的估计,一般还需要进行多次10折交叉验证求均值,例如:10次10折交叉验证,以求更精确一点。
#用准确率进行10折交叉验证选择合适的参数C
from sklearn.linear_model import LogisticRegressionCV
Cs = 10**np.linspace(-10, 10, 400)
lr_cv = LogisticRegressionCV(Cs=Cs, cv=10, penalty='l2', solver='saga', max_iter=10000, scoring='accuracy')
lr_cv.fit(X_train, y_train)
lr_cv.C_
LR = LogisticRegression(solver='saga', penalty='l2', C=25.52908068)
print(LR.fit(X_train, y_train))
print("训练集准确率: ", LR.score(X_train, y_train))
print("测试集准确率: ", LR.score(X_test, y_test))
2.3混淆矩阵
混淆矩阵的作用:
1)用于观察模型在各个类别上的表现,可以计算模型对应各个类别的准确率,召回率;
2)通过混淆矩阵可以观察到类别直接哪些不容易区分,比如A类别中有多少被分到了B类别,这样可以有针对性的设计特征等,使得类别更有区分性;
from sklearn import metrics
X_train_pred = LR.predict(X_train)
X_test_pred = LR.predict(X_test)
print('训练集混淆矩阵:')
print(metrics.confusion_matrix(y_train, X_train_pred))
print('测试集混淆矩阵:')
print(metrics.confusion_matrix(y_test, X_test_pred))
from sklearn.metrics import classification_report
print('训练集:')
print(classification_report(y_train, X_train_pred))
print('测试集:')
print(classification_report(y_test, X_test_pred))