项目介绍
近几年数据分析较为火热,现根据从BOSS直聘上爬取的最近数据分析岗位招聘信息,分析招聘信息,主要是清洗字段后从城市、工作年龄、学历、薪水等方面进行分析。
项目过程
- 数据清洗
- 该阶段主要是对项目脏数据进行清洗,包括去重、字段拆分、划分区间等操作。
1.1 position 字段清洗
1.2 work_year 字段清洗
1.3 city 字段清洗
1.4 text 字段清洗
- 数据分析阶段
2.1 数据类岗位整体需求
2.2 城市对薪资的影响
2.3 学历对薪资的影响
2.4 工作经验对薪资的影响
2.5 不同岗位对应的学历要求、薪水分布情况
0. 数据观察
从sql读取源数据,观察是否有缺失字段以及脏数据。
df=pd.read_sql_query('select * from Boss_analyst',con=engine)
字段解释:
- position - 职位
- salary_range - 薪水区间
- work_year - 工作经验
- city - 城市
- tag1 - 技能标签1
- tag2 - 技能标签2
- tag3 - 技能标签3
- tag4 - 技能标签4
- tag5 - 技能标签5
- company_name - 公司名称
- text - 未知
- company_type - 公司类型
- company_welfare -公司福利
- 一共有4482条数据,其中tag4、tag5有缺失(暂不考虑,可能仅仅是因为技能需求没那么多),company_welfare字段缺失;
- 其中position、salary_range、work_year、city、text均为脏数据,需要进行清洗。
1. 数据清洗
1.1 数据去重
由于可能爬取到重复数据,先对整体数据进行去重
df.drop_duplicates(inplace = True)
df.shape[0]
[out]:4435
- 其中有47条重复数据,删除后剩余4435条有效数据。
1.2 position 字段清洗
数据分析岗位筛选:
df.position.unique()
岗位名称中,也有许多也数据分析无关的岗位,需使用contains将与数据分析相关岗位筛选出来。
data = df[df.position.str.contains("数据|分析|挖掘|BI|算法")]
岗位格式统一:
- 观察数据可知,频次出现最多的关键字是数据分析师、数据运营 、数据挖掘、算法工程师
- 为了分析方便,将高级数据分析师之类的数据统一规整为数据分析
- 将数据运营专家类数据统一规整为数据运营
- 将数据挖掘工程师之类的数据统一规整为数据挖掘
- 其他还有商业分析师/ETL工程师之类的关键词出现不多,统一将其规整为其他
position_list = data.position.tolist()
# 使用正则进行匹配
str1 = ".*数据分析.*"
str2 = ".*数据挖掘.*"
str3 = ".*运营.*"
str4 = ".*算法.*"
# 创建一个空列表 用于接匹配结果
list2 = []
for i,values in enumerate(position_list):
# 使用正则对position_list中对应位置的元素进行匹配,若匹配成功则...
if re.match(str1,position_list[i]):
# 如果匹配str1成功,则执行以下代码
position_list[i] = "数据分析"
elif re.match(str2,position_list[i]):
position_list[i] = "数据挖掘"
elif re.match(str3,position_list[i]):
position_list[i] = "数据运营"
elif re.match(str4,position_list[i]):
position_list[i] = "算法工程师"
else:
position_list[i] = "其他"
# 重新赋值
data["position"] = pd.Series(position_list)
- 最后将岗位分为以上4种类。
1.2 salary_range 字段清洗
data.salary_range.unique()
- 工资字段种类较多,大致可分为:xx-xxk ·xx 薪,xx-xx元/天,xx-xxk 三种形式
- 现将他们进行清洗,划分为3列:最低薪资、最高薪资、平均薪资
# 创建函数,将薪资字段进行拆分
def cut_salary(word,types):
# 寻找“K”,“元”字符,如果找到返回所在位置,没找到则返回-1
position1 = word.find("K")
position2 = word.find("元")
if position1 != -1: # 如果找到“K”
salary_range = word[:position1]
elif position2 != -1: # 如果找到“元”
salary_range = word[:position2]
# 拆分数据,最低薪资、最高薪资
bottom_salary = int(salary_range.split("-")[0])
top_salary = int(salary_range.split("-")[1])
# 传递types参数,用于选择接受最小值还是最大值
if types == "min_salary":
return bottom_salary
elif types == "max_salary":
return top_salary
# running
data["min_salary"] = data.salary_range.apply(cut_salary,types = "min_salary")
data["max_salary"] = data.salary_range.apply(cut_salary,types = "max_salary")
#新增平均薪水列
data["avg_salary"] = (data["min_salary"] + data["max_salary"])/2
处理结果如下:
1.3 work_year 字段清洗
data.work_year.unique()
数据内容的格式大致有:
- 学历数据:学历不限、高中、中专/中技、大专、本科、硕士、博士
-
经验数据:3-5年、1年以内、10年以上、经验不限、4天/周3个月
由于4天/周类字段属于实习生,我们暂不考虑,筛选出全职工作,并将work_year字段划分为2个字段:工作年限、学历
python
# 切分学历要求
def cut_word2(word, types):
p1 = word.find("学历")
p2 = word.find("博")
p3 = word.find("硕")
p4 = word.find("本")
p5 = word.find("大")
p6 = word.find("高")
p7 = word.find("本")
p8 = word.find("中")
if p1 != -1:
work_year = word[:p1]
Edu = word[p1:]
elif p2 != -1:
work_year = word[:p2]
Edu = word[p2:]
elif p3 != -1:
work_year = word[:p3]
Edu = word[p3:]
elif p4 != -1:
work_year = word[:p4]
Edu = word[p4:]
elif p5 != -1:
work_year = word[:p5]
Edu = word[p5:]
elif p6 != -1:
work_year = word[:p6]
Edu = word[p6:]
elif p7 != -1:
work_year = word[:p7]
Edu = word[p7:]
elif p8 != -1:
work_year = word[:p8]
Edu = word[p8:]
# 根据传递的types值,接收经验年限或学历字段
if types == "work_year":
return work_year
elif types == "Edu":
return Edu
data["Edu"] = data.work_year.apply(cut_word2,types = "Edu")
data["work_year"] = data.work_year.apply(cut_word2,types = "work_year")
# 全职
data_full_time = data[~data.work_year.str.contains("天|周|月")]
data_full_time.head()
1.4 city 字段清洗
data_full_time.city.unique()
- 该字段中不仅划分了城市,还划分的区,暂不考虑区,统一划分为城市角度
data_full_time["city"] = data_full_time.city.apply(lambda x:x.split("·")[0])
1.5 text 字段清洗
# 观察text字段
data_full_time.text.unique()
- 该字段中大致有三类信息:公司行业、融资情况、公司规模
- 由于行业字段已经存在,先将他们分为融资情况和公司规模两列
查看无融资情况
data_full_time[~data_full_time.text.str.contains("天使|A|B|C|D|已上市|未融资|不需要")].shape[0]
[out]:127
- 有127条数据没有融资信息,因为数据量占比很小,且清洗复杂所以暂不处理
data = data_full_time[data_full_time.text.str.contains("天使|A|B|C|D|已上市|未融资|不需要")]
# 处理融资情况函数
# 对融资情况、公司规模做进行切片处理
def cut_word3(word, types):
# 融资情况
p1 = word.find("天")
p2 = word.find("A")
p3 = word.find("B")
p4 = word.find("C")
p5 = word.find("D")
p6 = word.find("已上市")
p7 = word.find("未融资")
p8 = word.find("不需要")
if p1 != -1:
company_financing = word[p1:p1+3]
company_size = word[p1+3:]
elif p2 != -1:
company_financing = word[p2:p2+2]
company_size = word[p2+2:]
elif p3 != -1:
company_financing = word[p3:p3+2]
company_size = word[p3+2:]
elif p4 != -1:
company_financing = word[p4:p4+2]
company_size = word[p4+2:]
elif p5 != -1:
company_financing = word[p5:p5+5]
company_size = word[p5+5:]
elif p6 != -1:
company_financing = word[p6:p6+3]
company_size = word[p6+3:]
elif p7 != -1:
company_financing = word[p7:p7+3]
company_size = word[p7+3:]
elif p8 != -1:
company_financing = word[p8:p8+5]
company_size = word[p8+5:]
# 根据types 接收
if types == "company_financing":
return company_financing
elif types == "company_size":
return company_size
data["company_financing"] = data.text.apply(cut_word3,types="company_financing")
data["company_size"] = data.text.apply(cut_word3,types="company_size")
# 检查数据
# 检查数据
print(data["company_financing"].unique())
print("- "*30)
print(data["company_size"].unique())
['已上市' '不需要融资' 'C轮' 'D轮及以上' 'A轮' '未融资' 'B轮' '天使轮']
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
['500-999人' '10000人以上' '1000-9999人' '100-499人' '20-99人' '0-20人']
- 清洗成功后
2. 数据分析
2.1 数据类型岗位整体状况
# 不同岗位占比
plt.rcParams["font.size"] = "16"
data.position.value_counts().plot(kind = "pie",figsize = (8,8),autopct='%.2f%%')
- 数据分析类岗位占了近一半,数据挖掘类次之,算法工程师类需求最少。但只是从大的范畴来区分的,实际会存在一定偏差,岗位名称叫法很多,其他类岗位中也会存在叫算法工程师,这就属于数据挖掘这块了。单看岗位名称不具体,要看具体的岗位职能描述才能准确定义这个岗位属于哪个范畴
plt.figure(figsize=(18,10))
plt.rcParams['font.size']=12
plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
#学历
plt.subplot(2,3,1)
data.Edu.value_counts().plot(kind='barh',alpha=0.7)
# 工作年限
plt.subplot(2,3,2)
data.work_year.value_counts().plot(kind='barh',alpha=0.7)
# 岗位分布情况
plt.subplot(2,3,3)
data.city.value_counts().plot(kind='pie',autopct='%.1f%%')
# 公司类别
plt.subplot(2,3,4)
data.company_type.value_counts().head(6).plot(kind='barh',alpha=0.7)
# 公司融资情况
plt.subplot(2,3,5)
data.company_financing.value_counts().plot(kind='barh',alpha=0.7)
# 薪水情况
bins = [0,3,6,9,12,15,20,30,100]
level = ["0-3k","3-6k","6-9k","9-12k","12-15k","15-20k","20-30k","30k+"]
data["salary_level"] = pd.cut(data.avg_salary,bins = bins,labels=level)
plt.subplot(2,3,6)
data["salary_level"].value_counts().plot(kind = "pie", autopct='%.1f%%')
plt.show()
- 学历要求:大专是最低要求,招高中或中专/中技的极少,最好是本科及以上
- 工作经验需求:偏向招聘有一定经验的求职者,尤其3-5年经验的需求最旺盛。一般工作3年以上,对于整个职业的了解会比较深入,技术趋于成熟,能够帮助做一些独立的项目
- 岗位分布情况:北上广深杭对其需求都差不多,相对来说北京机会最多,广州偏少
- 公司融资情况:招聘数据类岗位的一般都是达到了一定规模的大型企业
- 行业分布情况:互联网行业需求是最多的,包括电商、金融。还有一些乙方公司也有一定需求,比如数据服务类、咨询类
- 薪资情况:受工作经验影响较大,3年工作经验薪资一般集中在20-30K,比较可观
总结:
绝大部分公司不倾向出钱出力对新人进行培养,尤其近期又遭受到疫情的冲击,会更偏爱有工作经验的求职者,能够直接上手,尤其是能帮助做一些独立项目。
但这不仅仅针对于数据类岗位,所有岗位对求职者的要求都是越来越高的。工作经验这种要求,其实对于个人而言,它可以是硬性的,也可以是软性的,它其实是一种期望值,表示公司期望求职者能够匹配相应工作经验的能力,如果你的综合能力能够超过公司预期的期望,求职是完全没问题的
所以这就更要求我们对于能力方面做更多的训练,以及如何能够向公司体现你的能力做更多的准备
2.2 城市对薪资的影响
# 按城市聚合 所有工资级别数量
grouped_city = data.groupby(["city","salary_level"]).salary_level.count().unstack().fillna(0)
# 计算占比
grouped_city_diff = grouped_city.div(grouped_city.sum(1),axis = 0)
# 绘图
plt.rcParams['font.size'] = "13"
grouped_city_diff.plot(kind = "bar", stacked=True,figsize=(9,7),alpha=0.7,width=0.2)
# stacked = 表示是否堆积
plt.show()
- 北京愿意开出更高的价格吸引求职者,20-30K,甚至30K以上的比例都占了很大。
- 广州高薪资的比例没那么多,尤其10K以下的工资水平对比其他城市占的比例大
2.3 学历对薪资的影响
data.boxplot(column='avg_salary',by='Edu',figsize=(9,7))
- 学历上看,呈现出学历越高,起步价越高的态势(学历不限,可能是培训机构挂出来的岗位,也可能其他情况,这个不一概而论)
2.4 工作经验对薪资的影响
data.boxplot(column='avg_salary',by='work_year',figsize=(9,7))
- 工作经验上看,薪资的差距拉开的很大,应届生和有工作经验的薪资对比,不在一个维度上,但这恰恰说明数据类岗位是可以通过个人能力的提高来获取更大的收益的,它是可成长的。你对技术、业务越熟悉,你的机会就越多,薪资就越高。
2.5 不同岗位对学历的要求
各岗位的薪水分布情况
data.groupby(["salary_level","position"]).agg({"salary_level":"count"}).unstack().plot(figsize = (12,6))
- 总体分布情况上来看,数据分析、数据挖掘、数据运营岗位相对来说均在20-30k之间需求较多
- 数据挖掘类岗位对比其他岗位在30k+上占比最高,薪资集中于20-30k之间
各岗位的学历分布情况
data.groupby(["Edu","position"]).agg({"Edu":"count"}).unstack().plot(kind = "barh", figsize = (12,6))
- 数据挖掘类岗位对学历要求最高;
- 整体情况上,都是本科学历要求最多,所以最好有本科学历的基础转行数据分析。