问题描述:
我们有员工的各种统计信息,以及该员工是否已经离职,统计的信息包括了(工资、出差、工作环境满意度、工作投入度、是否加班、是否升职、工资提升比例等)现在需要你来通过训练数据得出 员工离职预测,并给出你在测试集上的预测结果。
数据集地址:https://www.kaggle.com/c/rs6-attrition-predict/data
数据说明:
训练数据和测试数据,保存在train.csv和test.csv文件中。训练集包括1176条记录,36个字段,字段说明如下:
字段 | 说明 | 字段 | 说明 |
---|---|---|---|
user_id | 用户ID,无意义特征 | MaritalStatus | 员工婚姻状况,Single单身,Married已婚,Divorced离婚 |
Age | 员工年龄 | MonthlyIncome | 员工月收入,范围在1009到19999之间 |
Attrition | 员工是否已经离职,Yes表示离职,No表示未离职 | MonthlyRate | 员工月收入 |
BusinessTravel | 商务差旅预测,Non-Travel不出差,TravelRarely不经常出差,TravelFrequently经常出差 | NumCompaniesWorked | 员工曾经工作过的公司数 |
DailyRate | 平均每日工资 | Over18 | 年龄是否超过18岁 |
Department | 员工所在部门,Sales销售部,Research & Development研发部,Human | OverTime | 是否加班,Yes表示加班,No表示不加班 |
DistanceFromHome | 公司跟家庭住址的距离,从1到29,1表示最近,29表示最远 | PercentSalaryHike | 工资提高的百分比 |
Education | 员工的教育程度,从1到5,5表示教育程度最高 | PerformanceRating | 绩效评估 |
EducationField | 员工所学习的专业领域 | RelationshipSatisfaction | 关系满意度,从1到4,1表示满意度最低,4表示满意度最高 |
EmployeeCount | 雇员人数 | StandardHours | 标准工时 |
EmployeeNumber | 工号 | StockOptionLevel | 股票期权水平 |
EnvironmentSatisfaction | 员工对于工作环境的满意程度,从1到4,1的满意程度最低,4的满意程度最高 | TotalWorkingYears | 总工龄 |
Gender | 员工性别,Male表示男性,Female表示女性 | TrainingTimesLastYear | 上一年的培训时长,从0到6,0表示没有培训,6表示培训时间最长 |
HourlyRate | 每小时收入 | WorkLifeBalance | 工作与生活平衡程度,从1到4,1表示平衡程度最低,4表示平衡程度最高 |
JobInvolvement | 员工工作投入度,从1到4,1为投入度最低,4为投入度最高 | YearsAtCompany | 在目前公司工作年数 |
JobLevel | 职业级别,从1到5,1为最低级别,5为最高级别 | YearsInCurrentRole | 在目前工作职责的工作年数 |
JobRole | 工作角色 | YearsSinceLastPromotion | 距离上次升职时长 |
JobSatisfaction | 工作满意度,从1到4,1代表满意度最低,4代表最高 | YearsWithCurrManager | 跟目前的管理者共事年数 |
评分标准:AUC
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split,cross_val_score
from sklearn.metrics import roc_auc_score
from imblearn.over_sampling import SMOTE
from sklearn.model_selection import GridSearchCV
data = pd.read_csv('../data/train.csv')
pd.set_option('display.max_columns', None)
data.head()
print("样本去重前样本数量:{}".format(data.shape[0]))
print("样本去重后样本数量:{}".format(data.drop_duplicates().shape[0]))
样本去重前样本数量:1176
样本去重后样本数量:1176
该数据集无重复数据
missingDf = data.isnull().sum().sort_values(ascending = False).reset_index()
missingDf.columns = ['feature','missing_num']
missingDf['missing_percentage'] = missingDf['missing_num'] / data.shape[0]
missingDf.head()
筛选数值型特征
numeric_columns = []
object_columns = []
for c in data.columns:
if data[c].dtype == 'object':
object_columns.append(c)
else:
numeric_columns.append(c)
绘制箱型图查看异常值
fig = plt.figure(figsize=(20,30))
for i,col in enumerate(numeric_columns):
ax = fig.add_subplot(9,3,i+1)
sns.boxplot(data[col],orient='v',ax=ax)
plt.xlabel(col)
plt.show()
此处截取了一些可能存在异常值的数值型特征,经过观察后,发现EmployeeCount特征数据全为1,StandardHours特征数据全为80,这些特征对模型无影响,之后要删除掉。此外,其他特征检验后无明显异常。此处以TotalWorkingYears特征为例:
data[data['TotalWorkingYears'] > 30][['Age','TotalWorkingYears']]
data.describe()
data.drop(['user_id','EmployeeCount','EmployeeNumber','StandardHours','Over18'],axis=1,inplace=True)
pearson_mat = data.corr(method='spearman')
plt.figure(figsize=(30,30))
ax = sns.heatmap(pearson_mat,square=True,annot=True,cmap='YlGnBu')
bottom, top = ax.get_ylim()
ax.set_ylim(bottom + 0.5, top - 0.5)
plt.show()
从图中可以看出,TotalWorkingYears(总工龄) 和 MonthlyIncome(月收入)相关性达到0.72,JobLevel(职业级别)和MonthlyIncome(月收入)相关性达到0.92,相关性很高。我们从常识来判断,月收入对员工离职的重要性是要大于其他两个特征的。所以,可以删除JobLevel和TotalWorkingYears两个特征,并认为月收入越高,员工级别越高,总工龄越高;
其次,YearsAtCompany(在目前公司工作年数) 和YearsInCurrentRole(在目前工作职责的工作年数) 、YearsWithCurrManager(跟目前的管理者共事年数)相关性过高,删除YearsInCurrentRole和YearsWithCurrManager两个特征。
PerformanceRating 和PercentSalaryHike相关性也过高,且PerformanceRating不明显影响员工离职率,所以删除PerformanceRating特征。
# PerformanceRating:绩效评估
fig = plt.figure(figsize=(15,4)) # 建立图像
L1 = list(data['PerformanceRating'].unique())
for i,c in enumerate(L1):
ax = fig.add_subplot(1,3,i+1)
p = data[data['PerformanceRating'] == c]['Attrition'].value_counts()
ax.pie(p,labels=['No','Yes'],autopct='%1.2f%%',explode=(0,0.2))
ax.set_title(c)
plt.show() # 展示饼状图
data.drop(['JobLevel','TotalWorkingYears','YearsInCurrentRole','YearsWithCurrManager','PerformanceRating'],axis=1,inplace=True)
商务差旅频率与是否离职的关系
# BusinessTravel:商务差旅频率
fig = plt.figure(figsize=(15,4)) # 建立图像
L1 = list(data['BusinessTravel'].unique())
for i,c in enumerate(L1):
ax = fig.add_subplot(1,3,i+1)
p = data[data['BusinessTravel'] == c]['Attrition'].value_counts()
ax.pie(p,labels=['No','Yes'],autopct='%1.2f%%',explode=(0,0.2))
ax.set_title(c)
plt.show() # 展示饼状图
可以看出,商务差旅数次数的增多对职工离职的影响也随之增大。出差月频繁,离职率越高。
加班与是否离职的关系
# OverTime
fig = plt.figure(figsize=(15,4)) # 建立图像
L1 = list(data['OverTime'].unique())
for i,c in enumerate(L1):
ax = fig.add_subplot(1,2,i+1)
p = data[data['OverTime'] == c]['Attrition'].value_counts()
ax.pie(p,labels=['No','Yes'],autopct='%1.2f%%',explode=(0,0.2))
ax.set_title(c)
plt.show() # 展示饼状图
工作满意度与是否离职的关系
# JobSatisfaction:工作满意度
fig = plt.figure(figsize=(20,8)) # 建立图像
L1 = list(data['JobSatisfaction'].unique())
for i,c in enumerate(L1):
ax = fig.add_subplot(2,3,i+1)
p = data[data['JobSatisfaction'] == c]['Attrition'].value_counts()
ax.pie(p,labels=['No','Yes'],autopct='%1.2f%%',explode=(0,0.2))
ax.set_title(c)
plt.show() # 展示饼状图
性别与是否离职的关系
# Gender
fig = plt.figure(figsize=(20,8)) # 建立图像
L1 = list(data['Gender'].unique())
for i,c in enumerate(L1):
ax = fig.add_subplot(2,3,i+1)
p = data[data['Gender'] == c]['Attrition'].value_counts()
ax.pie(p,labels=['No','Yes'],autopct='%1.2f%%',explode=(0,0.2))
ax.set_title(c)
plt.show() # 展示饼状图
我认为性别可能对离职率有影响但是不大。女性员工离职率可能较大。
员工所在部门与是否离职的关系
# Department 员工所在部门
fig = plt.figure(figsize=(15,4)) # 建立图像
L1 = list(data['Department'].unique())
for i,c in enumerate(L1):
ax = fig.add_subplot(1,3,i+1)
p = data[data['Department'] == c]['Attrition'].value_counts()
ax.pie(p,labels=['No','Yes'],autopct='%1.2f%%',explode=(0,0.2))
ax.set_title(c)
plt.show() # 展示饼状图
销售部门和HR部门的离职率较高。如果HR部门离职率较高,那么公司也很难长久的维持其他部门员工。
员工所学习的专业领域与是否离职的关系
# EducationField 员工所学习的专业领域
fig = plt.figure(figsize=(20,8)) # 建立图像
L1 = list(data['EducationField'].unique())
for i,c in enumerate(L1):
ax = fig.add_subplot(2,3,i+1)
p = data[data['EducationField'] == c]['Attrition'].value_counts()
ax.pie(p,labels=['No','Yes'],autopct='%1.2f%%',explode=(0,0.2))
ax.set_title(c)
plt.show() # 展示饼状图
# JobRole
fig = plt.figure(figsize=(20,8)) # 建立图像
L1 = list(data['JobRole'].unique())
for i,c in enumerate(L1):
ax = fig.add_subplot(2,5,i+1)
p = data[data['JobRole'] == c]['Attrition'].value_counts()
ax.pie(p,labels=['No','Yes'],autopct='%1.2f%%',explode=(0,0.2))
ax.set_title(c)
plt.show() # 展示饼状图
员工的婚姻状况与是否离职的关系
# MaritalStatus
fig = plt.figure(figsize=(15,4)) # 建立图像
L1 = list(data['MaritalStatus'].unique())
for i,c in enumerate(L1):
ax = fig.add_subplot(1,3,i+1)
p = data[data['MaritalStatus'] == c]['Attrition'].value_counts()
ax.pie(p,labels=['No','Yes'],autopct='%1.2f%%',explode=(0,0.2))
ax.set_title(c)
plt.show() # 展示饼状图
# Attrition
data['Attrition'] = data['Attrition'].apply(lambda x:1 if x == "Yes" else 0)
# Gender
data['Gender'] = data['Gender'].apply(lambda x:1 if x == "Male" else 0)
# OverTime
data['OverTime'] = data['OverTime'].apply(lambda x:1 if x == "Yes" else 0)
for fea in ['BusinessTravel', 'Department', 'EducationField','JobRole','MaritalStatus']:
labels = data[fea].unique().tolist()
data[fea] = data[fea].apply(lambda x:labels.index(x))
X = data.loc[:,data.columns != "Attrition"]
y = data['Attrition']
y.value_counts()
0 988
1 188
Name: Attrition, dtype: int64
数据量过少,使用上采样处理
sm = SMOTE(random_state=20)
X, y = sm.fit_sample(X,y)
X = pd.DataFrame(X)
y = pd.DataFrame(y)
Xtrain,Xtest,Ytrain,Ytest = train_test_split(X, y ,test_size = 0.3,random_state=0)
model = DecisionTreeClassifier(random_state=0)
model.fit(Xtrain,Ytrain)
pred = model.predict(Xtest)
y_pred_prob = model.predict_proba(Xtest)[:, 1]
auc_score = roc_auc_score(Ytest,y_pred_prob)#验证集上的auc值
auc_score
0.838745131090408
gini_thresholds = np.linspace(0,0.5,20)
parameters = {
'splitter':('best','random')
,'criterion':("gini","entropy")
,"max_depth":[*range(1,10)]
,'min_samples_leaf':[*range(1,50,5)]
,'min_impurity_decrease':[*np.linspace(0,0.5,20)]
}
clf = DecisionTreeClassifier(random_state=25)
GS = GridSearchCV(clf, parameters, cv=10,scoring='roc_auc')
GS.fit(Xtrain,Ytrain)
最优评分:
GS.best_score_
0.9235445387585528
最优参数:
GS.best_params_
{‘criterion’: ‘gini’,
‘max_depth’: 9,
‘min_impurity_decrease’: 0.0,
‘min_samples_leaf’: 11,
‘splitter’: ‘best’}
model = DecisionTreeClassifier(random_state=0,criterion='gini',max_depth=9,min_impurity_decrease=0,min_samples_leaf=11,splitter='best')
model.fit(Xtrain,Ytrain)
model.score(Xtest,Ytest)
0.8549747048903878
pred = model.predict(Xtest)
y_pred_prob = model.predict_proba(Xtest)[:, 1]
auc_score = roc_auc_score(Ytest,y_pred_prob)#验证集上的auc值
auc_score
0.9232078995922646