美文网首页
DC员工离职预测训练赛

DC员工离职预测训练赛

作者: henrywongo | 来源:发表于2018-11-10 14:00 被阅读0次

    该比赛为DC练习赛,要求使用逻辑回归的方法,从给定的影响员工离职的因素和员工是否离职的记录,建立一个逻辑回归模型预测有可能离职的员工。

    相关数据的介绍,请参考:比赛地址

    1.探索数据

    import numpy as np
    import pandas as pd
    
    # 读取数据
    train = pd.read_csv('pfm_train.csv')
    test = pd.read_csv('/pfm_test.csv')
    print('train size:{}'.format(train.shape))  # train size:(1100, 31)
    print('test size:{}'.format(test.shape))  #test size:(350, 30)
    # 查看数据集中是否含有缺失值:无缺失值
    # train.isnull().mean()
    

    1.1 数据分析

    # EmployeeNumber为员工ID,将其删除
    train.drop(['EmployeeNumber'], axis = 1, inplace = True)
    
    # 将Attrition(该字段为标签)移至最后一列,方便索引
    Attrition = train['Attrition']
    train.drop(['Attrition'], axis = 1, inplace = True)
    train.insert(0, 'Attrition', Attrition)
    

    使用pyecharts从各维度上对离职人数以及离职率进行分析

    from pyecharts import Bar,Line,Grid
    from pyecharts import Overlap
    
    # 通过图表分析哪些因素是主要影响员工离职的因素
    def get_chatrs(train, col):
        data = train.groupby([col])['Attrition']
        data_sum = data.sum() # 离职人数
        data_mean = data.mean()  # 离职率
        
        bar = Bar(col, title_pos="45%")
        bar.add('离职人数', data_sum.index, data_sum.values, mark_point = ['max'],
                yaxis_formatter =  '人', yaxis_max = 200 , legend_pos="40%", legend_orient="vertical", 
                legend_top="95%", bar_category_gap = '25%')
    
        line = Line()
        line.add('离职率', data_mean.index, data_mean.values, mark_point = ['max'], mark_line = ['average'],
            yaxis_max = 0.8)
    
        overlap = Overlap(width=900, height=400)
        overlap.add(bar)
        overlap.add(line, is_add_yaxis=True, yaxis_index=1)
    
        return overlap
        
    
    from pyecharts import Page
    page = Page()
    for col in train.columns[1:]:
        page.add(get_chatrs(train, col))
    page.render('pages.html')
    page
    

    运行此段代码后,发现图表数据显示有错误,检查代码没有发现问题,手动的在图表中刷新数据后,问题得到解决

    # 公司总体的离职率在16.2%
    train['Attrition'].mean()
    

    通过观察图表发现以下问题
    Q1研发部门离职人数最多,这主要是因为该公司研发部门人数最多的原因,虽然人数多,但是研发部门离职率最低,离职率最高的部门是HR,该部门也是公司人数最少的部门,人员架构不太稳定?(综合其他因素如’Education‘、’JobRole‘等,也发现,HRD离职率很高

    Department.png

    Q2: 18岁-23岁员工离职率超过40%,23岁-33岁员工离职率在20%-40%

    Age.png

    Q3: 工作投入度为1等级的员工离职率有近40%,达到了38%!!!

    JobInvolvement.png

    Q4: 加班的员工离职率是不加班员工的三倍!!!

    OverTime.png

    进一步探索

    部门&加班&收入
    收入在较低水平的销售部门员工在加班的况下离职率达80%,且该部门员工在加班情况下,无论收入水平如何,离职率都高于公司的整体离职率 部门工作满意度
    HRD员工离职的原因之一:工作满意度比较低,是否存在办公室政治的原因?

    2.数据处理&特征处理

    2.1 数据处理

    # 在分析中发现有一些字段的值是单一的,进一步验证
    single_value_feature = []
    for col in train.columns:
        lenght = len(train[col].unique())
        if lenght == 1:
            single_value_feature.append(col)
    
    single_value_feature  # ['Over18', 'StandardHours']
    

    'Over18', 'StandardHours'这两个字段的值是唯一的,删除这两个字段

    # 删除这两个字段
    train.drop(['Over18', 'StandardHours'], axis = 1, inplace = True)
    train.shape  # (1100, 28)
    

    由于数据集中没有缺失值,这里不需要对缺失值做处理

    2.2 特征处理

    主要是对部分特征进行分组以及one-hot编码

    # 对收入进行分箱
    print(train['MonthlyIncome'].min())  # 1009
    print(train['MonthlyIncome'].max())  # 19999
    print(test['MonthlyIncome'].min())  # 1051
    print(test['MonthlyIncome'].max())  # 19973
    

    为了在train和test中的MonthlyIncome进行分组后的区间一致,需要保持两个数据集中MonthlyIncome的最大值和最小值一致,这里使用等宽分组

    由于test数据集中MonthlyIncome的最小值比train数据集中的最小值大,最大值比train数据集中的最大值小,需要人工插入最大值最小值后才能进行分组,这样在test数据集中MonthlyIncome的分组区间才能与train中MonthlyIncome分组一致,这个后面会进行具体操作

    # 使用pandas的cut进行分组,分为10组
    train['MonthlyIncome'] = pd.cut(train['MonthlyIncome'], bins=10)
    
    # 将数据类型为‘object’的字段名提取出来,并使用one-hot-encode对其进行编码
    col_object = []
    for col in train.columns[1:]:
        if train[col].dtype == 'object':
            col_object.append(col)
    col_object
    

    对train数据集进行one-hot编码

    train_encode = pd.get_dummies(train)
    

    保存数据集,方便日后使用

    train.to_csv('trainwithoutencode.csv')
    train_encode.to_csv('train.csv')
    

    2.3 特征共线性处理

    corr = train.corr()
    
    import matplotlib.pyplot as plt
    import seaborn as sns
    %matplotlib inline
    
    sns.heatmap(corr, xticklabels=corr.columns.values, yticklabels=corr.columns.values)
    plt.show()
    
    特征共线性
    'TotalWorkingYears' & 'JobLevel'
    'YearsAtCompany' & 'YearsWithCurrManager'存在共线性,选择删除其中一个特征即可
    train_encode.drop(['TotalWorkingYears', 'YearsWithCurrManager'], axis = 1, inplace = True)
    

    3. 建模预测

    3.1 建模

    from sklearn.linear_model import LogisticRegression
    from sklearn.model_selection import train_test_split
    
    X = train_encode.iloc[:, 1:]
    y = train_encode.iloc[:, 0]
    
    # 划分训练集以及测试集
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2)
    
    lr = LogisticRegression()
    lr.fit(X_train, y_train)
    lr.score(X_train, y_train)  # 0.8886363636363637
    

    由于存在随机性,最终在训练集上的score大约在0.88~0.9波动

    pred = lr.predict(X_test)
    np.mean(pred == y_test)  # 0.8863636363636364
    

    结果在测试集上的结果与训练集差不多,下面看一下预测结果的混淆矩阵是怎样的

    from sklearn.metrics import confusion_matrix
    from sklearn.metrics import precision_score, recall_score, f1_score
    
    #对整个train数据集的混淆矩阵
    y_pred = lr.predict(X)
    confmat= confusion_matrix(y_true=y,y_pred=y_pred)#输出混淆矩阵
    fig,ax = plt.subplots(figsize=(2.5,2.5))
    ax.matshow(confmat,cmap=plt.cm.Blues,alpha=0.3)
    for i in range(confmat.shape[0]):
        for j in range(confmat.shape[1]):
            ax.text(x=j,y=i,s=confmat[i,j],va='center',ha='center')
    plt.xlabel('predicted label')
    plt.ylabel('true label')
    plt.show()
    
    #召回率、准确率、F1
    print ('precision:%.3f' %precision_score(y_true=y,y_pred=y_pred))
    print ('recall:%.3f' %recall_score(y_true=y,y_pred=y_pred))
    print ('F1:%.3f' %f1_score(y_true=y,y_pred=y_pred))
    
    混淆矩阵
    发现准确率和召回率都是很很满意,然后尝试调参,lr中可调整的参数不多,调整后发现模型的精度提高不是很大

    3.2 预测

    # test数据集处理
    test.drop(['EmployeeNumber', 'Over18', 'StandardHours'], axis = 1, inplace = True)
    test_MonthlyIncome = pd.concat((pd.Series([1009, 19999]), test['MonthlyIncome'])) 
    # 在指定位置插入与train中MonthlyIncome的max、min一致的数值,之后再删除
    test['MonthlyIncome'] = pd.cut(test_MonthlyIncome, bins=10)[2:]  # 分组并去除对应的值
    test_encode = pd.get_dummies(test)
    test_encode.drop(['TotalWorkingYears', 'YearsWithCurrManager'], axis = 1, inplace = True)# 输出结果
    sample = pd.DataFrame(lr.predict(test_encode))
    sample.to_csv('sample.csv')
    

    按照要求修改sample的格式后上传

    结果排名
    上传结果后,排在60名,top5%,还算可以的结果

    4. 反思

    • 对于逻辑回归,在参数上调整空间比较小,应该注重在特征工程上的处理,除了使用one-hot编码的方法外,还可以尝试使用归一化、标准表等等,使用交叉验证的方式查看模型的稳定性
    • 也可以使用随机森林、GBDT等方法

    相关文章

      网友评论

          本文标题:DC员工离职预测训练赛

          本文链接:https://www.haomeiwen.com/subject/tvrzxqtx.html