員工離職,彷佛已經成爲每一家企業都要面對的問題,特別是優秀人才離職的問題會讓領導特別頭疼。今天咱們就經過kaggle上某一家企業員工離職的真實數據來對離職率進行分析建模,並對預測結果顯示要離職的員工提出挽留建議。html
1. 數據來源及背景算法
2. 明確分析目的app
3. 數據探索分析echarts
4. 數據預處理dom
5. 可視化分析機器學習
6. 特徵工程ide
7. 邏輯迴歸模型學習
8. 樸素貝葉斯模型測試
9. 模型評估之ROC曲線優化
數據來源: https://www.kaggle.com/jiangzuo/hr-comma-sep/version/1
數據背景: 該數據集是指某公司員工的離職數據, 其包含14999個樣本以及10個特徵, 這10個特徵分別爲: 員工對公司滿意度, 最新考覈評估, 項目數, 平均每個月工做時長, 工做年限, 是否出現工做事故, 是否離職, 過去5年是否升職, 崗位, 薪資水平.
該數據集討論的是公司員工離職的問題, 那麼, 咱們首先須要對影響員工離職的因素進行分析:
將上述影響因素與現有的數據相結合來提出問題,進而明確咱們的分析目的:
1) 員工對公司滿意度平均水平如何?員工的最新考覈狀況又是如何?員工所參加項目數是怎樣?員工平均每個月工做時長以及平均工做年限分別是多少?
2) 當前離職率是多少?工做事故發生率?過去5年升職率?薪資水平又如何?共有多少種崗位?
3) 是否離職和其餘9個特徵的關係如何?
4) 根據現有數據, 如何對某個員工是否離職進行預測?
5) 針對當前的員工離職狀況,企業該如何對待呢?
1) 查看前2行和後2行數據
import pandas as pd df = pd.read_csv(r'D:\Data\HR_comma_sep.csv') pd.set_option('display.max_rows', 4) df
數據維度14999行×10列, 除過崗位和薪資水平是字符型外, 其他均是數字 (具體是什麼類型還須要進一步肯定).
2) 查看數據類型等信息
df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 14999 entries, 0 to 14998 Data columns (total 10 columns): satisfaction_level 14999 non-null float64 last_evaluation 14999 non-null float64 number_project 14999 non-null int64 average_montly_hours 14999 non-null int64 time_spend_company 14999 non-null int64 Work_accident 14999 non-null int64 left 14999 non-null int64 promotion_last_5years 14999 non-null int64 sales 14999 non-null object salary 14999 non-null object dtypes: float64(2), int64(6), object(2) memory usage: 1.1+ MB
前兩個特徵爲64位浮點型, 後兩個爲字符型, 其他爲64位整型, 且均完好失值.
3). 描述性統計
df.describe() df.describe(include=['O']).T
1) 員工對公司滿意度平均水平如何?員工的最新考覈狀況又是如何?員工所參加項目數是怎樣?員工平均每個月工做時長以及平均工做年限分別是多少?
員工對公司的滿意度: 範圍 0.09~1, 中位數0.640, 均值0.613, 整體來講員工對公司較滿意
最新考覈評估: 範圍 0.36~1, 中位數0.720, 均值0.716, 員工考覈平均水平中等偏上.
項目數: 範圍 2~7個, 中位數4, 均值3.8, 平均參加項目數爲4個
平均每個月工做時長: 範圍96~310小時, 中位數200, 均值201
工做年限: 範圍2~10年, 中位數3, 均值3.5
2) 當前離職率是多少?工做事故發生率?過去5年升職率?薪資水平又如何?共有多少種崗位?
當前離職率爲23.81%
工做事故發生率14.46%.
過去5年升職率2.13%.
薪資水平共有3個等級, 最多的是低等, 多達7316人.
員工崗位有10種, 其中最多的是銷售, 多達4140人.
沒有缺失值, 所以不用處理缺失值.
1. 異常值
經過箱線圖查看異常值.
import seaborn as sns fig, ax = plt.subplots(1,5, figsize=(12, 2)) sns.boxplot(x=df.columns[0], data=df, ax=ax[0]) sns.boxplot(x=df.columns[1], data=df, ax=ax[1]) sns.boxplot(x=df.columns[2], data=df, ax=ax[2]) sns.boxplot(x=df.columns[3], data=df, ax=ax[3]) sns.boxplot(x=df.columns[4], data=df, ax=ax[4])
除了工做年限外, 其餘均無異常值. 該異常值也反映了該公司員工中以年輕人爲主
在這裏經過可視化的形式對第三個問題「是否離職和其餘9個特徵的關係如何?」進行回答.
1. 人力資源整體狀況
from pyecharts import Pie attr = ["離職", "在職"] v1 =[df.left.value_counts()[1], df.left.value_counts()[0]] pie = Pie("該公司人力資源整體狀況", title_pos='center') pie.add( "", attr, v1, radius=[35, 65], label_text_color=None, is_label_show=True, legend_orient="vertical", legend_pos="left", ) pie.render()
離職3571人, 在職11428人, 離職率爲23.81%
2. 對公司滿意度與是否離職的關係
from pyecharts import Boxplot #字段重命名 df.columns=['satisfaction', 'evaluation', 'project', 'hours', 'years_work','work_accident', 'left', 'promotion', 'department', 'salary'] #繪製箱線圖 boxplot = Boxplot("對公司滿意度與是否離職關係圖", title_pos='center') x_axis = ['在職', '離職'] y_axis = [df[df.left == 0].satisfaction.values, df[df.left == 1].satisfaction.values] boxplot.add("", x_axis, boxplot.prepare_data(y_axis)) boxplot.render()
就中位數而言, 離職人員對公司滿意度相對較低, 且離職人員對公司滿意度總體波動較大. 另外離職人員中沒有滿意度爲1的評價.
3. 最新考覈評估與是否離職的關係
boxplot = Boxplot("最新評估與是否離職關係圖", title_pos='center') x_axis = ['在職', '離職'] y_axis = [df[df.left == 0].evaluation.values, df[df.left == 1].evaluation.values] boxplot.add("", x_axis, boxplot.prepare_data(y_axis)) boxplot.render()
就中位數而言, 離職人員的最新考覈評估相對較高, 但其波動也大.
4. 所參加項目與是否離職的關係
from pyecharts import Bar, Pie, Grid #按照項目數分組分別求離職人數和全部人數 project_left_1 = df[df.left == 1].groupby('project')['left'].count() project_all = df.groupby('project')['left'].count() #分別計算離職人數和在職人數所佔比例 project_left1_rate = project_left_1 / project_all project_left0_rate = 1 - project_left1_rate attr = project_left1_rate.index bar = Bar("所參加項目數與是否離職的關係圖", title_pos='10%') bar.add("離職", attr, project_left1_rate, is_stack=True) bar.add("在職", attr, project_left0_rate, is_stack=True, legend_pos="left", legend_orient="vertical") #繪製圓環圖 pie = Pie("各項目數所佔百分比", title_pos='center') pie.add('', project_all.index, project_all, radius=[35, 60], label_text_color=None, is_label_show=True, legend_orient="vertical", legend_pos="67%") grid = Grid(width=1200) grid.add(bar, grid_right="67%") grid.add(pie) grid.render()
經過下圖能夠發現如下2點:
- 離職率隨着項目數的增多而增大, 2個項目數是特例
- 離職率較高的項目數2, 6, 7在總項目數中所佔百分比相對較少. 項目數爲2的這部分人多是工做能力不被承認, 其離職人數也相對較多; 項目數爲6, 7的這部分人一方面體現的是工做能力較強, 另外一方面也說明了工做強度大, 其可能在其餘企業能有更好的發展, 天然離職率也相對較高.
5. 平均每個月工做時長和是否離職的關係
boxplot = Boxplot("平均每個月工做時長與是否離職關係圖", title_pos='center') x_axis = ['在職', '離職'] y_axis = [df[df.left == 0].hours.values, df[df.left == 1].hours.values] boxplot.add("", x_axis, boxplot.prepare_data(y_axis)) boxplot.render()
經過下圖能夠看到: 離職人員的平均每個月工做時長相對較長, 每個月按照22個工做日計算, 每日工做時數的中位數爲10.18小時, 最大值爲14.09小時.
6. 工做年限和是否離職的關係
from pyecharts import Bar, Pie, Grid #按照工做年限分別求離職人數和全部人數 years_left_0 = df[df.left == 0].groupby('years_work')['left'].count() years_all = df.groupby('years_work')['left'].count() #分別計算離職人數和在職人數所佔比例 years_left0_rate = years_left_0 / years_all years_left1_rate = 1 - years_left0_rate attr = years_all.index bar = Bar("工做年限與是否離職的關係圖", title_pos='10%') bar.add("離職", attr, years_left1_rate, is_stack=True) bar.add("在職", attr, years_left0_rate, is_stack=True, legend_pos="left" , legend_orient="vertical") #繪製圓環圖 pie = Pie("各工做年限所佔百分比", title_pos='center') pie.add('', years_all.index, years_all, radius=[35, 60], label_text_color=None, is_label_show=True, legend_orient="vertical", legend_pos="67%") grid = Grid(width=1200) grid.add(bar, grid_right="67%") grid.add(pie) grid.render()
經過下圖能夠得出:
- 在各工做年限中, 離職人員較集中於3, 4, 5, 6年, 而6年以上則相對穩定
- 企業中工做年限爲3年的人數所佔百分比最多, 其次是2年, 主要以年輕人爲主
7. 是否發生工做事故與是否離職的關係
from pyecharts import Bar accident_left = pd.crosstab(df.work_accident, df.left) attr = accident_left.index bar = Bar("是否發生工做事故與是否離職的關係圖", title_pos='center') bar.add("離職", attr, accident_left[1], is_stack=True) bar.add("在職", attr, accident_left[0], is_stack=True, legend_pos="left" , legend_orient="vertical", is_label_show=True) bar.render()
能夠看到少部分出現工做事故, 且其中有較少部分人離職.
8. 5年內是否升職與是否離職的關係
promotion_left = pd.crosstab(df.promotion, df.left) attr = promotion_left.index bar = Bar("5年內是否升職與是否離職的關係圖", title_pos='center') bar.add("離職", attr, promotion_left[1], is_stack=True) bar.add("在職", attr, promotion_left[0], is_stack=True, legend_pos="left" , legend_orient="vertical", is_label_show=True) bar.render()
5年內多數人沒有升職, 離職率就相對較高.
9. 崗位與是否離職的關係
#分別計算各崗位離職人員比例和各崗位佔整體百分比 department_left_0 = df[df.left == 0].groupby('department')['left'].count() department_all = df.groupby('department')['left'].count() department_left0_rate = department_left_0 / department_all department_left1_rate = 1 - department_left0_rate attr = department_all.index bar = Bar("崗位與離職比例的關係圖", title_top='40%') bar.add("離職", attr, department_left1_rate, is_stack=True) bar.add("在職", attr, department_left0_rate, is_stack=True, is_datazoom_show=True, xaxis_interval=0, xaxis_rotate=30, legend_top="45%", legend_pos="80%") #繪製圓環圖 pie = Pie("各個崗位所佔百分比", title_pos='left') pie.add('', department_all.index, department_all,center=[50, 23], radius=[18, 35], label_text_color=None, is_label_show=True, legend_orient="vertical", legend_pos="80%", legend_top="4%") grid = Grid(width=1200, height=700) grid.add(bar, grid_top="50%", grid_bottom="25%") grid.add(pie) grid.render()
經過下圖能夠看出:
- 在全部崗位中銷售崗位人數所佔百分比最多, 達到27.6%, 最少是管理層, 其所佔百分比是4.2%
- 使人意外的是崗位中hr離職率最高.
10. 薪資水平和是否離職的關係
from pyecharts import Bar #按照薪資水平分別求離職人數和全部人數 salary_left = pd.crosstab(df.salary, df.left).sort_values(0, ascending = False) attr = salary_left.index bar = Bar("薪資水平和是否離職的關係圖", title_pos='center') bar.add("離職", attr, salary_left[1], is_stack=True) bar.add("在職", attr, salary_left[0], is_stack=True, legend_pos="left" , legend_orient="vertical", is_label_show=True) bar.render()
薪資分爲三個水平: 低等, 中等, 高等. 低等水平離職人數最多, 所佔比例也最大, 而高等則最少.
在回答第4個問題以前,須要對數據進行特徵處理工做,以知足模型對數據的要求
1. 離散型數據處理
離散型數據可分爲兩種: 一種是定序, 一種是定類.
1) 定序
薪資水平其含有順序意義, 所以將其字符型轉化爲數值型
df['salary'] = df.salary.map({"low": 0, "medium": 1, "high": 2}) df.salary.unique()
array([0, 1, 2], dtype=int64)
2) 定類
崗位是定類型變量, 對其進行one-hot編碼, 這裏直接利用pandas的get_dummies方法.
df_one_hot = pd.get_dummies(df, prefix="dep") df_one_hot.shape
(14999, 19)
2. 連續型數據處理
邏輯迴歸模型可以適應連續型變量, 所以能夠不用進行離散化處理, 又因爲多個特徵之間差別差別較大會形成梯度降低算法收斂速度變慢, 故進行歸一化處理
#採用max-min歸一化方法 hours = df_one_hot['hours'] df_one_hot['hours'] = df_one_hot.hours.apply(lambda x: (x-hours.min()) / (hours.max()-hours.min()))
3. 相關係數
兩個變量均是連續型且具備線性關係, 則可使用皮爾遜相關係數, 不然使用斯皮爾曼相關係數, 這裏採用斯皮爾曼相關係數
#計算相關係數 correlation = df_one_hot.corr(method = "spearman") plt.figure(figsize=(18, 10)) #繪製熱力圖 sns.heatmap(correlation, linewidths=0.2, vmax=1, vmin=-1, linecolor='w',fmt='.2f', annot=True,annot_kws={'size':10},square=True)
接下來就到了解釋第四個問題的環節了, 下面將採用兩種模型來對員工是否離職進行預測, 這兩個模型分別是邏輯迴歸模型和樸素貝葉斯模型
1. 劃分數據集
from sklearn.model_selection import train_test_split #劃分訓練集和測試集 X = df_one_hot.drop(['left'], axis=1) y = df_one_hot['left'] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1)
2. 訓練模型
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))
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=100, multi_class='warn', n_jobs=None, penalty='l2', random_state=None, solver='warn', tol=0.0001, verbose=0, warm_start=False) 訓練集準確率: 0.7978998249854155 測試集準確率: 0.7966666666666666
參考官方文檔說明, 參數C是正則化項參數的倒數, C的數值越小, 懲罰的力度越大. penalty可選L1, L2正則化項, 默認是L2正則化.
參數solver可選{‘newton-cg’, ‘lbfgs’, ‘liblinear’, ‘sag’, ‘saga’}這5個優化算法:
newton-cg, lbfgs是擬牛頓法, liblinear是座標軸降低法, sag, saga是隨機梯度降低法, saga能夠適用於L1和L2正則化項, 而sag只能用於L2正則化項.
#指定隨機梯度降低優化算法 LR = LogisticRegression(solver='saga') print(LR.fit(X_train, y_train)) print("訓練集準確率: ", LR.score(X_train, y_train)) print("測試集準確率: ", LR.score(X_test, y_test))
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True, intercept_scaling=1, max_iter=100, multi_class='warn', n_jobs=None, penalty='l2', random_state=None, solver='saga', tol=0.0001, verbose=0, warm_start=False) 訓練集準確率: 0.7980665055421285 測試集準確率: 0.7973333333333333
在選擇隨機梯度降低法後, 訓練集和測試集準確率均略有提高.
3. 調參
#用準確率進行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_
array([25.52908068])
用該參數進行預測
LR = LogisticRegression(solver='saga', penalty='l2', C=25.52908068) print("訓練集準確率: ", LR.score(X_train, y_train)) print("測試集準確率: ", LR.score(X_test, y_test))
訓練集準確率: 0.7984832069339112 測試集準確率: 0.798
訓練集和測試集準確率均有所提高, 對於二分類問題, 準確率有時不是很好的評估方法, 這時須要用到混淆矩陣
4. 混淆矩陣
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))
訓練集混淆矩陣: [[8494 647] [1771 1087]] 測試集混淆矩陣: [[2112 175] [ 431 282]]
from sklearn.metrics import classification_report print('訓練集:') print(classification_report(y_train, X_train_pred)) print('測試集:') print(classification_report(y_test, X_test_pred))
訓練集: precision recall f1-score support 0 0.83 0.93 0.88 9141 1 0.63 0.38 0.47 2858 micro avg 0.80 0.80 0.80 11999 macro avg 0.73 0.65 0.67 11999 weighted avg 0.78 0.80 0.78 11999 測試集: precision recall f1-score support 0 0.83 0.92 0.87 2287 1 0.62 0.40 0.48 713 micro avg 0.80 0.80 0.80 3000 macro avg 0.72 0.66 0.68 3000 weighted avg 0.78 0.80 0.78 3000
在訓練集有0.83的精準率和0.93的召回率, 在測試集上有0.83的精準率和0.92的召回率.
樸素貝葉斯模型是基於特徵條件獨立假設和貝葉斯理論.
from sklearn.naive_bayes import GaussianNB from sklearn.model_selection import cross_val_score #構建高斯樸素貝葉斯模型 gnb = GaussianNB() gnb.fit(X_train, y_train) print("訓練集準確率: ", gnb.score(X_train, y_train)) print("測試集準確率: ", gnb.score(X_test, y_test)) X_train_pred =gnb.predict(X_train) X_test_pred = gnb.predict(X_test) print('訓練集混淆矩陣:') print(metrics.confusion_matrix(y_train, X_train_pred)) print('測試集混淆矩陣:') print(metrics.confusion_matrix(y_test, X_test_pred)) print('訓練集:') print(classification_report(y_train, X_train_pred)) print('測試集:') print(classification_report(y_test, X_test_pred))
訓練集準確率: 0.7440620051670973 測試集準確率: 0.741 訓練集混淆矩陣: [[6791 2350] [ 721 2137]] 測試集混淆矩陣: [[1680 607] [ 170 543]] 訓練集: precision recall f1-score support 0 0.90 0.74 0.82 9141 1 0.48 0.75 0.58 2858 micro avg 0.74 0.74 0.74 11999 macro avg 0.69 0.75 0.70 11999 weighted avg 0.80 0.74 0.76 11999 測試集: precision recall f1-score support 0 0.91 0.73 0.81 2287 1 0.47 0.76 0.58 713 micro avg 0.74 0.74 0.74 3000 macro avg 0.69 0.75 0.70 3000 weighted avg 0.80 0.74 0.76 3000
能夠看到其準確率較邏輯迴歸低, 可是精準率高於邏輯迴歸.
from sklearn import metrics from sklearn.metrics import roc_curve #將邏輯迴歸模型和高斯樸素貝葉斯模型預測出的機率均與實際值經過roc_curve比較返回假正率, 真正率, 閾值 lr_fpr, lr_tpr, lr_thresholds = roc_curve(y_test, LR.predict_proba(X_test)[:,1]) gnb_fpr, gnb_tpr, gnb_thresholds = roc_curve(y_test, gnb.predict_proba(X_test)[:,1]) #分別計算這兩個模型的auc的值, auc值就是roc曲線下的面積 lr_roc_auc = metrics.auc(lr_fpr, lr_tpr) gnb_roc_auc = metrics.auc(gnb_fpr, gnb_tpr) plt.figure(figsize=(8, 5)) plt.plot([0, 1], [0, 1],'--', color='r') plt.plot(lr_fpr, lr_tpr, label='LogisticRegression(area = %0.2f)' % lr_roc_auc) plt.plot(gnb_fpr, gnb_tpr, label='GaussianNB(area = %0.2f)' % gnb_roc_auc) plt.xlim([0.0, 1.0]) plt.ylim([0.0, 1.0]) plt.title('ROC') plt.xlabel('FPR') plt.ylabel('TPR') plt.legend() plt.show()
ROC曲線越靠近左上角說明分類效果越好, 與之對應的auc的值就越大. 對於該數據集來講, 高斯樸素貝葉斯模型略優於邏輯迴歸模型
以上就是對第四題的解釋, 下面進入第五題
5) 針對當前的員工離職狀況,企業該如何對待呢?
該公司的員工離職現狀:
1.當前離職率爲23.81%
2.離職人員對公司滿意度廣泛較低
3.離職人員的考覈成績相對較高, 說明離職人員多數爲優秀人才.
4.項目數範圍爲2~7個, 其中參加7個項目的離職率最高,其次是2個的; 7個的工做能力較強, 在其餘企業有更好的發展, 2個的多是在該公司中工做能力不被承認
5.離職人員的平均每個月工做時長較長
6.離職人員的工做年限集中在3到6年
7.5年內未升職的離職率較高
8.hr崗位的離職率最高, 目前企業廣泛存在"留人難, 招人難」,這多是致使該崗位的離職率高的主要緣由
9.低等薪資水平的離職率最高
因爲企業培養人才是須要大量的成本, 爲了防止人才再次流失, 所以應當注重解決人才的流失問題, 也就是留人, 另外若是在招人時注意某些問題, 也能在必定程度上減小人才流失. 所以, 這裏可將對策分爲兩種, 一種是留人對策, 一種是招人對策.
留人對策:
1.創建良好的薪酬制度, 不得低於市場水平
2.創建明朗的晉升機制
3.完善獎懲機制, 能者多勞, 也應多得.
4.實現福利多樣化, 增長員工對企業的忠誠度
5.重視企業文化建設, 樹立共同的價值觀
6.改善辦公環境以及營造良好的工做氛圍
7.鼓勵員工自我提高
招人對策:
1.明確企業招聘需求, 員工的能力應當與崗位需求相匹配
2.與應聘者坦誠相見
3.招聘期間給予的相關承諾必須實現
4.歡迎優秀流失人才迴歸
本文從邏輯迴歸模型的原理開始介紹, 並經過實際案例對邏輯迴歸模型進行應用, 可是結果還不是很好, 一方面是模型表現不是很好(固然, 也僅僅用到了邏輯迴歸和樸素貝葉斯) ; 另外一方面是特徵工程沒有處理好(樸素貝葉斯模型對特徵條件獨立假設較敏感), 應當進行特徵選擇, 好比主成分分析.
參考資料:
網易雲課堂《吳恩達機器學習》
《Python數據分析與挖掘實戰》
聲明: 僅用於學習交流