最近在做Python
职位分析的项目,做这件事的背景是因为接触Python
这么久,还没有对Python
职位有一个全貌的了解。所以想通过本次分析了解Python
相关的职位有哪些、在不同城市的需求量有何差异、薪资怎么样以及对工作经验有什么要求等等。分析的链路包括:
-
数据采集
-
数据清洗
- 异常的创建时间
- 异常的薪资水平
- 异常的工作经验
- 统计分析
- 大盘数据
- 单维度分析
- 二维交叉分析
- 多维钻取
- 文本分析
- 文本预处理
- 词云
- FP-Growth关联分析
- LDA主题模型分析
分为上下两篇文章。上篇介绍前三部分内容,下篇重点介绍文本分析。
0. 数据采集
巧妇难为无米之炊,我们做数据分析大部分情况是用公司的业务数据,因此就不需要关心数据采集的问题。然而我们自己业余时间做的一些数据探索更多的需要自己采集数据,常用的数据采集技术就是爬虫
。
本次分享所用的数据是我从拉勾网爬取的,主要分为三部分,确定如何抓取数据、编写爬虫抓取数据、将抓取的数据格式化并保存至MongoDB
。关于数据采集这部分内容我之前有一篇文章单独介绍过,源码也开放了,这里我就不再赘述了,想了解的朋友可以翻看之前那篇文章《Python爬职位》。
1. 数据清洗
有了数据后,先不要着急分析。我们需要对数据先有个大概的了解,并在这个过程中剔除一些异常的记录,防止它们影响后续的统计结果。
举个例子,假设有101个职位,其中100个的薪资是正常值10k,而另外一个薪资是异常值1000k,如果算上异常值计算的平均薪资是29.7k,而剔除异常值计算的平均薪资是10k,二者差了将近3倍。
所以我们在作分析前要关注数据质量,尤其数据量比较少的情况。本次分析的职位数有1w条左右,属于比较小的数据量,所以在数据清洗这一步花了比较多的时间。
下面我们就从数据清洗开始,进入编码阶段
1.0 筛选python相关的职位
导入常用库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pymongo import MongoClient
from pylab import mpl
mpl.rcParams['font.sans-serif'] = ['SimHei'] #解决seaborn中文字体显示问题
%matplotlib inline
复制代码
从MongoDB
读取数据
mongoConn = MongoClient(host='192.168.29.132', port=27017)
db = mongoConn.get_database('lagou')
mon_data = db.py_positions.find()
# json转DataFrame
jobs = pd.json_normalize([record for record in mon_data])
复制代码
预览数据
jobs.head(4)
复制代码
打印出jobs
的行列信息
jobs.info()
复制代码
一共读取了1.9w个岗位,但这些岗位里并不都是跟Python
相关的。所以我们首先要做的就是筛选Python
相关的职位,采用的规则是职位标题或正文包含python
字符串
# 抽取职位名称或者职位正文里包含 python 的
py_jobs = jobs[(jobs['pName'].str.lower().str.contains("python")) | (jobs['pDetail'].str.lower().str.contains("python"))]
py_jobs.info()
复制代码
筛选后,只剩下10705个岗位,我们继续对这部分岗位进行清洗。
1.1 按照创建时间清洗异常值
对 “职位创建时间” 维度清洗主要是为了防止有些创建时间特别离谱的岗位混进来,比如:出现了2000年招聘的岗位。
# 创建一个函数将职位创建时间戳转为月份
import time
def timestamp_to_date(ts):
ts = ts / 1000
time_local = time.localtime(ts)
return time.strftime("%Y-%m", time_local)
# 增加'职位创建月份'一列
py_jobs['createMon'] = py_jobs['createTime'].map(timestamp_to_date)
# 按照职位id、创建月份分组计数
py_jobs[['pId', 'createMon']].groupby('createMon').count()
复制代码
不同月的职位
创建timestamp_to_date 函数将“职位创建时间”转为“职位创建月份”,然后按“职位创建月份”分组计数。从结果上看,职位创建的时间没有特别离谱的,也就是说没有异常值。即便如此,我仍然对职位创建时间进行了筛选,只保留了10、11、12三个月的数据,因为这三个月的职位占了大头,并且我只想关注新职位。
# 只看近三个月的职位
py_jobs_mon = py_jobs[py_jobs['createMon'] > '2020-09']
复制代码
1.2 按照薪资清洗异常值
对薪资进行清洗主要是防止某些职位的薪资特别离谱。这块主要考察3个特征:薪资高的离群点、薪资低的离群点和薪资跨度较大的。
首先,列出所有的薪资
py_jobs_mon[['pId', 'salary']].groupby('salary').count().index.values
复制代码
以薪资高的离群点为例,观察是否有异常值
# 薪资高的离群值
py_