数据分析行业薪资的秘密,你想知道的都在这里(5)

第五部分:数据分析职位建模及薪资预测

数据分析师的收入怎么样?哪些因素对于数据分析的薪资影响最大?哪些行业对数据分析人才的需求量最高?我想跳槽,应该选择大公司大平台还是初创的小公司?按我目前的教育程度,工作经验,和掌握的工具和技能,能获得什么样水平的薪资呢?

我们使用python抓取了2017年6月26日拉钩网站内搜索“数据分析”关键词下的450条职位信息。通过对这些职位信息的分析和建模来给你答案。

desktop-and-app-virtualization-predictions-vmware-euc

本系列文章共分为五个部分,分别是数据分析职位信息抓取,数据清洗及预处理,数据分析职位分布分析,数据分析薪资影响因素分析,以及数据建模和薪资预测。这是第五篇:数据分析职位建模及薪资预测。

建模及预测前的准备工作

导入所需的库文件,这里主要包含5类分别为用于数值计算和处理的numpy和pandas库。用于特征处理和选择的DictVectorizer和SelectFromModel库。各种分类算法库,包括KNN,DecisionTree, RandomForest,和GaussianNB等。以及用于交叉验证和计算召回率的cross_validation和recall_score库。和用于保存模型的joblib库。

#数据计算及处理
import numpy as np
import pandas as pd
 
#特征处理
from sklearn.feature_extraction import DictVectorizer
 
#特征选择
from sklearn.feature_selection import SelectFromModel
 
#分类算法
from sklearn.neural_network import MLPClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC
from sklearn.svm import LinearSVC
from sklearn.gaussian_process import GaussianProcessClassifier
from sklearn.gaussian_process.kernels import RBF
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysis
from sklearn.neural_network import MLPClassifier
 
#交叉检验
from sklearn import cross_validation
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
 
#模型持久化
from sklearn.externals import joblib

 

导入清洗及处理过的数据表,我们将使用这个数据表进行建模和预测。

#导入清洗及预处理后的数据表
lagou=pd.DataFrame(pd.read_csv('lagou_clear_data_1.csv',header=0,encoding='GBK'))

 

特征处理及选择

从原数据表中提取一部分字段作为我们的特征X,这里既包括数据表中原有的字段,也有经过我们清洗,处理和提取生成的字段。对特征X进行OneHot编码,具体的原理和过程请参考我之前的文章。另外,将聚类的标签作为我们的分类目标Y。

#特征处理
X_df=lagou[['workYear', 'education', 'city', 'companySize',  'industry_1', 
            'financeStage1', 'positionName1',  'sql', 'python', 'excel', 
            'spss', 'matlab', 'sas', 'r', 'hadoop', 'spark', 'tableau',
            'tool_num', 'jd_num','diversity']]
X_list=X_df.to_dict(orient="records")
vec = DictVectorizer()
X=vec.fit_transform(X_list)
Y=np.array(lagou['cluster_label'])

查看编码后的特征及维度数据。

#特征维度
X.toarray(),X.shape

 

原始特征维度
下一步对特征进行选择。随机森林算法可以对特征的重要性进行打分,下面是具体的代码和所有特征的重要性得分。

#特征重要性
clf = RandomForestClassifier()
clf = clf.fit(X, Y)
feature_importances=clf.feature_importances_
feature_names=vec.get_feature_names()
 
s=[[0 for i in range(2)] for x in range(len(feature_names))]
columns_name=['names','importances']
for i in range(len(feature_names)):
    s[i][0]=feature_names[i]
    s[i][1]=feature_importances[i]
feature1=pd.DataFrame(s,columns=columns_name)
print(feature1.sort_values(by=['importances'],ascending=0))

 

 

names importances
29 diversity 0.093784
62 jd_num 0.087956
84 workYear=5-10年 0.063155
80 tool_num 0.051393
83 workYear=3-5年 0.049671
64 positionName1=专员 0.041187
72 positionName1=经理 0.027778
35 excel 0.027471
3 city=北京 0.024657
77 spss 0.022108
70 positionName1=工程师 0.021393
73 python 0.020377
26 companySize=2000人以上 0.019582
60 industry_1=移动互联网 0.019245
44 hadoop 0.019052
27 companySize=50-150人 0.017329
74 r 0.016619
76 spark 0.016320
39 financeStage1=D轮 0.016127
65 positionName1=专家 0.016044
28 companySize=500-2000人 0.016028
85 workYear=不限 0.015798
81 workYear=1-3年 0.015461
75 sas 0.014598
41 financeStage1=不需要融资 0.014326
67 positionName1=其他 0.013337
33 education=本科 0.012510
43 financeStage1=未融资 0.011864
0 city=上海 0.011590
36 financeStage1=A轮 0.011335
.. … …
6 city=合肥 0.002343
47 industry_1=信息安全 0.002108
50 industry_1=广告营销 0.002029
55 industry_1=游戏 0.001993
69 positionName1=实习 0.001537
79 tableau 0.001515
48 industry_1=其他 0.001462
68 positionName1=助理 0.001208
49 industry_1=医疗健康 0.000948
2 city=佛山 0.000918
21 city=郑州 0.000794
56 industry_1=生活服务 0.000758
59 industry_1=社交网络 0.000713
54 industry_1=旅游 0.000658
51 industry_1=教育 0.000605
11 city=成都 0.000553
53 industry_1=文化娱乐 0.000525
17 city=珠海 0.000494
23 city=长沙 0.000390
19 city=苏州 0.000252
20 city=邯郸 0.000124
1 city=东莞 0.000113
14 city=济南 0.000000
9 city=宁波 0.000000
8 city=大连 0.000000
7 city=嘉兴 0.000000
16 city=烟台 0.000000
82 workYear=1年以下 0.000000
31 education=博士 0.000000
18 city=福州 0.000000

[87 rows x 2 columns]
也可以使用L1对特征进行选择,下面是具体的代码和经过选择的新特征X_new以及L1挑选出的特征名称。

#特征选择
lsvc = LinearSVC(C=0.03, penalty="l1", dual=False).fit(X, Y)
model = SelectFromModel(lsvc, prefit=True)
X_new = model.transform(X)
vec.restrict(model.get_support())
 
DictVectorizer(dtype=<class 'numpy.float64'>, separator='=', sort=True,
sparse=True)
#选择后的特征维度
X_new.toarray(),X_new.shape
选择后的特征维度
#选择后的特征名称 
vec.get_feature_names()

选择后的特征名称

 

完成特征选择后,将新特征随机划分为训练集数据和测试集数据。这里训练集数据60%,测试集数据40%。

对模型进行训练

将选择后的特征和分类结果随机划分为训练集和测试集数据。

#划分训练集和测试集数据
X_train,X_test,y_train,y_test=cross_validation.train_test_split(X_new.toarray(),Y,test_size=0.4,random_state=0)

使用训练集数据对模型进行训练。这里我们不太确定那一种算法效果会更好一些,因此训练集数据对10个不同算法的分类器依次进行训练,选出score最高的模型使用。

#设置算法名称
names = ["Nearest Neighbors", "Linear SVM", "RBF SVM", "Gaussian Process",
         "Decision Tree", "Random Forest", "Neural Net", "AdaBoost",
         "Naive Bayes", "QDA"]
#设置算法
classifiers = [
    KNeighborsClassifier(3),
    SVC(kernel="linear", C=0.025),
    SVC(gamma=2, C=1),
    GaussianProcessClassifier(1.0 * RBF(1.0), warm_start=True),
    DecisionTreeClassifier(max_depth=5),
    RandomForestClassifier(max_depth=5, n_estimators=10, max_features=1),
    MLPClassifier(alpha=1),
    AdaBoostClassifier(),
    GaussianNB(),
    QuadraticDiscriminantAnalysis()]
#逐个训练并获得不同算法的score
for name, clf in zip(names, classifiers):
        clf.fit(X_train,y_train)
        score = clf.score(X_test, y_test)
        print(name,score)

逐个训练并获得不同算法的score

 

按照上面的结果来看,MLPClassifier的score最高。因此我们使用这个模型来进行分类预测。建立一个MLPC分类器并对他进行训练。

建立及保存模型

#建立MLPC分类器
clf = MLPClassifier(solver='lbfgs', alpha=1e-5,hidden_layer_sizes=(5, 2), random_state=1)

 

#对模型进行训练
clf.fit(X_train,y_train)

 

对模型进行训练

#对模型进行测试
clf.score(X_test, y_test)

 

对模型进行测试

计算这个模型的召回率recall及f1_score

#对测试集进行预测并保存到y_pred
y_pred=[]
for i in X_test:
    y_pred.append(clf.predict(i))
#计算模型recall
recall_score(y_test,y_pred, average='macro')

 

计算模型recall

#计算模型f1_score
f1_score(y_test, y_pred, average='macro')

计算模型f1_score

 

对测试集数据进行预测,查看分类概率以及真实分类结果。

#对测试集的第一组特征进行预测
clf.predict(X_test[0])
 
array([0], dtype=int64)

模型对测试集第一组特征的分类,50.5%的概率为第一类,11.1%概率为第二类,38。2%的概率为第三类。因此将这组特征的结果分为第一类。

 

#测试集第一组特征分组概率
clf.predict_proba(X_test[0])
array([[ 0.5065032 , 0.11132312, 0.38217368]])

测试集第一组特征的真实分类结果也是第一类。

#第一组特征的真实分类标签
y_test[0]
0

最后是对模型的保存以及导入,方便后续的使用和预测。给模型起一个名字,然后保存为pkl文件。

#保存模型
joblib.dump(clf, 'lagou_MLP.pkl')
 
['lagou_MLP.pkl']

再次使用时直接导入这个模型文件就可以开始进行预测了。这里我们以测试集的第二组特征为例,使用保存的模型进行分类预测。

#导入模型
clf_1 = joblib.load('lagou_MLP.pkl') 
#对第二组特征进行分类预测
clf_1.predict(X_test[1])
 
array([0], dtype=int64)

如果你觉得只获得薪资分组的预测结果过于模糊了,想直接预测薪资的数值,可以使用可以使用回归算法。这里我们以决策树回归为例,对模型进行训练,并对具体的薪资数值进行预测。

#导入决策树算法
from sklearn import tree
#特征处理
X_df=lagou[['workYear', 'education', 'city', 'companySize',  'industry_1', 
            'financeStage1', 'positionName1',  'sql', 'python', 'excel', 
            'spss', 'matlab', 'sas', 'r', 'hadoop', 'spark', 'tableau',
            'tool_num', 'jd_num','diversity']]
X_list=X_df.to_dict(orient="records")
vec = DictVectorizer()
X=vec.fit_transform(X_list)
#目标值设置为薪资均值
Y=np.array(lagou['salary_avg'])
#划分训练集和测试集数据
X_train,X_test,y_train,y_test=cross_validation.train_test_split(X_new.toarray(),Y,test_size=0.4,random_state=0)
#对决策树回归模型进行训练
clf = tree.DecisionTreeRegressor()
clf = clf.fit(X_train, y_train)
#对测试集的第一组特征进行预测
clf.predict(X_test[0])
array([ 11.5])
#查看测试集实际目标值
y_test[0]
14.0

 

最后,利用GraphViz对决策树回归的训练结果和预测过程进行可视化。下面是具体的代码和决策树图框。(为了便于显示我们设置max_depth=3)

决策树框图显示了一系列的节点,这些节点分为两类,一类针对问题输出“是”或者“否”,另外一类是终止节点,输出预测结果,并终止整个决策的过程。以下是具体的代码和部分的决策树图框截图。

from sklearn import tree
clf = tree.DecisionTreeRegressor(max_depth=3)
clf = clf.fit(X_train, y_train)
 
with open("clf.dot", 'w') as f:
    f = tree.export_graphviz(clf, out_file=f)
 
import os
os.unlink('clf.dot')
 
import pydotplus 
dot_data = tree.export_graphviz(clf, out_file=None) 
graph = pydotplus.graph_from_dot_data(dot_data) 
graph.write_pdf("lagou_DecisionTreeRegressor.pdf")

决策树可视化

本篇文章我们对数据分析职位的薪资创建了一个粗糙的分类模型,并使用模型对不同特征的职位特征和要求进行薪资分类预测。这可以根据你所选择职位的属性和你的个人能力预测出大致的薪资水平区间。到这里我们完成了从信息抓取,到数据提取分析,以及最终建模预测的整个闭环。同时我们获得了一份450个职位的职位需求列表,一些数据分析职位的需求分布情况,通过了解影响数据分析师薪资影响因素获得了未来的发展方向,以及一个基于450个职位的薪资预测模型。

但你可能会问,这个预测结果准确吗?答案是不够准确。无论是分类模型还是回归模型,都还有很多可以优化和提升的空间。以下提高模型准确率的一些方法,就是我们下一步要做的工作。

  1. 增加更多的数据——我们在数据抓取中是否遗漏了重要的职位信息?
  2. 处理缺失值和异常值——我们对薪资简单的平均化是否存在不妥?对职级分类中大部分没有title的数据分析和数据分析师职位被归类为其他是否存在问题?
  3. 特征工程学——职位描述字段是否有更多可以提取的信息?比如不同薪资类别的独有关键词?
  4. 特征选择——我们选择特征的方法是否存在问题?
  5. 使用多种算法——我们选择模型的方法和使用的模型是否符合这类数据?
  6. 算法的调整——本篇文章中并没有对模型进行调参。调整后准确率会有多高的提升?
  7. 集成学习(Ensemble learning)——本篇文章没有采用多个分类器对数据集进行预测。

—【所有文章及图片版权归 蓝鲸(王彦平)所有。欢迎转载,但请注明转自“蓝鲸网站分析博客”。】—

Speak Your Mind

*