俗话说读万卷书不如行万里路,提升编程能力的很好途径之一是自己去写,但当自己不会的时候去解析别人写的代码也是效率不错的学习途径。
本文将逐行解读大神Alfred数据室里的一项爬取招聘网站的代码,因小弟并非计算机专业,只是略有兴趣,遗漏之处,必为不会,错误不足之处,请不吝指点。
完整代码在文末。
没经验没学历的外教为啥能拿1.4W+的高薪?

本文主要学习pandas等库在数据分析中的作用,同时使用pyecharts来制作可视化交互图标,也略微接触了matplotlib和正则表达式的使用。

  • 原作者原始文件为 ipython 文件,但我的 notebook 不知为何无法显示其中的图片,因此将代码放到 python_file 了。完整代码可直接运行,但只能一次生成一个文件,因此要将后面的代码分别注释运行,如果有同学有方法一次运行得到,请不吝指点。
  • pyecharts 版本为旧版本,可输入 $ pip install pyecharts==0.5.6 下载。
  • 附录的完整代码是我修改过的,原始代码可以在原作者 github 主页下载。
  • pyecharts 在 jupyter lab 与 notebook 上的用法有些许区别,具体可以根据版本查阅官方文档,旧版本与新版本
  • 文中数据获取办法有一部分来自之前一篇文章,其他数据可以在原作者 github 上获取。
  • 本文与原文数据可能不太一样,原因在于部分数据重新抓取,由于时效原因,不尽一致。
  • 进行数据分析前,数据的清洗极其极其重要!今天血的教训,编程代码出问题后找了很长时间才发现是数据问题。
  • 数据清洗的部分,需要看到真实的数据才会知道该把杂乱的数据清洗成可以进行分析的数据,所以只看本部分解析难以有直观的认识,非常建议读者将数据下载下来后对照使用。

文章目录

    • 代码分析
      • 输入数据
      • 数据清洗
        • 中国教师
        • 外教
      • 数据分析
        • 洋外教的工资真的高吗?
        • 市场对于洋外教的经验和学历要求如何?
        • 哪些地区对洋外教的需求多?
        • 什么机构在招聘洋外教?
        • 洋外教来源真的很乱吗?
    • 知识总结
      • re库
      • pandas与numpy库
      • matplotlib库
      • pyecharts库
    • 完整代码

代码分析

import re
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from pylab import mpl
from pyecharts import Bar
from pyecharts import WordCloud
from pyecharts import Pie

导入第三方库。
其中 re 是正则表达式。
pandas,numpy 用来数据分析。
matplotlib 用来绘图。
seaborn 是基于 matplotlib 的图形可视化 python 包,画出来的图更具交互性。
pylab 是 matplotlib 提供的一个模块,常在 ipython 上用。
pyecharts 生成的 echarts 类图标非常好看,且很有交互性。

输入数据

data_kdgd = pd.read_csv("幼儿园.csv")
data_pmsc = pd.read_csv("中小学.csv")
data_trn = pd.read_csv("外语培训.csv")
data_clg = pd.read_csv("职业院校.csv")
data_jlc = pd.read_csv("jobleadchina.csv", encoding='ISO-8859-1')
data_gm = pd.read_csv("data_gm.csv")

# 把来自万行教师的四个数据集组合成一个Dataframe
data_kdgd['type'] = '幼儿园'
data_pmsc['type'] = '中小学'
data_trn['type'] = '外语培训'
data_clg['type'] = '职业院校'
data_wx = pd.concat([data_kdgd, data_pmsc, data_trn, data_clg])

pd.read 是用 pandas 读取文件的函数,还可以读取 excel, hdf 等类型文件,直接将后面的 csv 改成 excel, hdf 即可。
encoding 涉及到编码问题,不太懂。原因在于自己手动清洗了个别数据,以后为了方便起见,读取时直接加上类似代码。
data_x[‘x’] = ‘x’ 在x文件新建“x”列,里面集体赋值“x”。
.concat() 是 pandas 用来合并不同表格的函数。

数据清洗

中国教师

# 1. 清洗出省份、城市

data_wx['area'].str.split("-", expand=True)
data_wx['province'] = data_wx['area'].str.split("-", expand=True)[0]
data_wx['city'] = data_wx['area'].str.split("-", expand=True)[1]

# 把北京、天津、上海、重庆的城市改为原来的名字
data_wx.loc[data_wx['province'] == '北京', 'city'] = '北京'
data_wx.loc[data_wx['province'] == '上海', 'city'] = '上海'
data_wx.loc[data_wx['province'] == '天津', 'city'] = '天津'
data_wx.loc[data_wx['province'] == '重庆', 'city'] = '重庆'

str.split("-", expand=True), pandas 里用来分隔字符串的函数,以“”里的内容为分割,如本来数据是“广东-广州”这样的,分割后会变成“广东,广州”(csv文件以 ‘,’为分隔符)。
后面两行代码的意思是将第1位的元素命名为‘province’,第二位的元素命名为‘city’(通常0代表第一位)。
.loc[], 根据元素的选取条件来选取对应的数据集,即修正直辖市的数据显示。

# 2. 清洗出经验、学历

data_wx['exp'] = data_wx['exp_title'].str.split("/", expand=True)[0]
data_wx['degree'] = data_wx['exp_title'].str.split("/", expand=True)[1]
data_wx['exp'].unique()
exp_map = {'不限':'经验不限', '一年以上':'一到三年', '三年以上':'三到五年', '两年以上':'一到三年',
           '五年以上':'五到十年', '应届毕业生':'经验不限', '六年以上':'五到十年', '四年以上':'三到五年',
           '九年以上':'五到十年', '七年以上':'五到十年', '十年以上':'十年以上', '在读学生':'经验不限',
           '八年以上':'五到十年'}
data_wx['exp'] = data_wx['exp'].map(exp_map)
data_wx['degree'].unique()
degree_map = {'大专':'大专', '不限':'学历不限', '大学本科以上':'本科', '大学本科':'本科',
              '大专以上':'大专', '不限以上':'学历不限', '中专以上':'中专', '硕士以上':'硕士',
              '硕士':'硕士', '高中以上':'高中', '中专':'中专'}
data_wx['degree'] = data_wx['degree'].map(degree_map)

unique 函数去除其中重复的元素,并按元素由大到小返回一个新的无元素重复的元组或者列表。
map() 函数根据提供的函数对指定序列做映射,比如将‘不限’替换成‘经验不限’。

# 3.清洗薪水
data_wx['salary'].unique()


def get_salary(data):
    pat_K = r"(.*)K-(.*)K"
    pat_W = r"(.*)W-(.*)W"
    pat = r"(.*)-(.*)/"
    if '面议' in data:
        return np.nan
    if '享公办教师薪资待遇' in data:
        return np.nan
    if 'K' in data and '月' in data:
        low, high = re.findall(pattern=pat_K, string=data)[0]
        return (float(low)+float(high))/2
    if 'W' in data and '年' in data:
        low, high = re.findall(pattern=pat_W, string=data)[0]
        return (float(low)+float(high))/2*10/12
    if 'K' not in data and '月' in data:
        low, high = re.findall(pattern=pat, string=data)[0]
        return (float(low)+float(high))/2/1000


data_wx['salary_clean'] = data_wx['salary'].apply(get_salary)
data_wx['salary_clean'] = np.round(data_wx['salary_clean'], 1)

由 unique 函数得到爬取的薪资数据,该函数是将不规范的薪资表达变成统一的数据。
该函数用到了正则表达式,需要的同学可以查看此链接。
re.findall() 在字符串中找到正则表达式所匹配的所有子串,并返回一个列表。
以上函数是为了清洗爬取到的复杂的薪资数据,截取部分数据如下,以更加直观的认识:

‘10W-12W/年’, ‘3.5K-7K/月’, ‘6K-10K/月’, ‘享公办教师薪资待遇’, ‘5K-10K/月’,
‘8K-15K/月’, ‘5K-8K/月’, ‘7K-10K/月’, ‘8K-10K/月’, ‘6K-8K/月’,
‘8W-12W/年’, ‘5K-6K/月’, ‘4K-6K/月’, ‘9W-12W/年’, ‘4.5K-6K/月’,
‘4K-8K/月’, ‘8W-8W/年’, ‘4K-5.5K/月’, ‘3K-5K/月’, ‘4.5K-8K/月’,
‘2.8K-3.5K/月’, ‘6K-12K/月’, ‘6W-9W/年’, ‘10W-15W/年’, ‘10K-15K/月’,
‘4K-7K/月’, ‘3K-6K/月’, ‘8W-8.5W/年’, ‘6K-7.5K/月’, ‘800-1000/月’,
‘0.8W-1W/年’, ‘2K-3K/月’, ‘6W-12W/年’, ‘7K-12K/月’, ‘2.6K-5K/月’,

data_wx[‘salary’].apply(get_salary) 即对“salary”这一列采取该函数。
np.round(, []) 返回某列数据某位浮点数。

外教

# 1. 清洗出exp_title
data_jlc['exp_title'].unique()
data_jlc['exp_title_clean'] = data_jlc['exp_title'].str.split(": ", expand=True)[1]
# 2. 清洗出salary
data_jlc['salary'].unique()


def get_salary_jlc(data):
    pat_jlc = r"(.*)K/MTH - (.*)K/MTH"
    if '00' in data:
        low, high = re.findall(pattern=pat_jlc, string=data)[0]
        return (float(low)+float(high))/2/1000
    else:
        low, high = re.findall(pattern=pat_jlc, string=data)[0]
        return (float(low)+float(high))/2


data_jlc['salary_clean'] = data_jlc['salary'].apply(get_salary_jlc)

在此处为何分情况处理,看到原始数据即知原因:

‘15000K/MTH - 20000K/MTH’, ‘13K/MTH - 26K/MTH’,
‘13K/MTH - 18K/MTH’, ‘10000K/MTH - 20000K/MTH’,
‘15K/MTH - 22K/MTH’, ‘8000K/MTH - 20000K/MTH’, ‘20K/MTH - 25K/MTH’,
‘16K/MTH - 23K/MTH’, ‘8K/MTH - 14K/MTH’, ‘15K/MTH - 18K/MTH’,

# 微信群成员数据清洗
data_gm = data_gm[['NickName', 'City', 'Province', 'Sex', 'Signature']]
data_gm.to_csv("data_gm.csv", index=False)

数据分析

  1. 洋外教的工资真的高吗?
  2. 市场对于洋外教的经验和学历要求如何?
  3. 哪些地区对洋外教的需求多?
  4. 什么机构在招聘洋外教?
  5. 洋外教来源真的很乱吗?

洋外教的工资真的高吗?

data_wx.drop(data_wx[data_wx['salary_clean'] > 40].index, inplace=True)

data_wx['teacher_type'] = '中教'
data_jlc['teacher_type'] = '外教'
data_salary = pd.concat([data_wx[['salary_clean', 'teacher_type']],
                        data_jlc[['salary_clean', 'teacher_type']]])

data_salary.rename(columns={'salary_clean': '工资', 'teacher_type': '教师类型'}, inplace=True)
# sns.set(font_scale=1.5)
g = sns.FacetGrid(data_salary, row="教师类型", height=4, aspect=2, xlim=(0, 30))
g.map(sns.distplot, "工资", rug=False)
plt.show()

此处去掉了极端值对数据的影响,使用 seaborn 进行画图:

由于 Seaborn 是在 matplotlib 的基础上进行了封装,因此画图时使用 plt.show().

市场对于洋外教的经验和学历要求如何?

np.round(data_jlc['salary_clean'].mean(), 1)
np.round(data_wx['salary_clean'].mean(), 1)
data_wx.groupby('city')['salary_clean'].mean().mean()
np.round(data_wx.groupby('city')['salary_clean'].mean().sort_values()[:10], 1)

# 通过经验进行对比
data_jlc.loc[data_jlc['exp_title_clean']=='Associate', 'exp_title_clean'] = 'Entry Level'
np.round(data_jlc.groupby('exp_title_clean')['salary_clean'].mean(), 1)
np.round(data_wx.groupby('exp')['salary_clean'].mean(), 1)


attr = ['经验不限\nInternship', '一到三年\nEntry Level', '三到五年\nExecutive',
        '五到十年\nMid-Senior', '十年以上\nDirector']
value1 = [10.1, 15.8, 14.0, 15.8, 20.9]
value2 = [7.8, 8.0, 10.1, 13.6, 21.6]
bar1 = Bar("不同工作经验的英语外教与中教工资对比", width="700px",height= "500px" )
bar1.add("外教", attr, value1, xaxis_label_textsize=18, legend_top=30,
         yaxis_label_textsize=20, is_label_show=True)
bar1.add("中教", attr, value2, xaxis_label_textsize=18, legend_top=30,
         yaxis_label_textsize=20, is_label_show=True)
bar1.render()

此处使用 pyecharts 作图,attr 与 value 是作图所需要的元素,新建柱状图 bar1, 给予“名字、宽度、高度”的赋值,再添加两条数据,里面的元素分别为“名字、横坐标名称、数值、横坐标标签字体大小、图例组件离容器上侧的距离、纵坐标标签字体大小、是否正常显示标签”。

# 通过学历进行对比
np.round(data_jlc.groupby('education')['salary_clean'].mean(), 1)
data_wx.loc[data_wx['degree'] == '中专', 'degree'] = '高中'
np.round(data_wx.groupby('degree')['salary_clean'].mean(), 1)

attr = ['学历不限\nAny education', '高中', '大专\nAssociate',
         '本科\nBachelor', '硕士\nMaster']
value1 = [13.9, np.nan, 12.8, 16.3, 21.3]
value2 = [7.5, 4.4, 6.4, 9.2, 11.0]
bar = Bar("不同学历的英语外教与中教工资对比", width = 700,height=500)
bar.add("外教", attr, value1, xaxis_label_textsize=15, legend_top=30,
         yaxis_label_textsize=20, is_label_show=True)
bar.add("中教", attr, value2, xaxis_label_textsize=15, legend_top=30,
         yaxis_label_textsize=20, is_label_show=True)
bar.render()

  • 市场对于洋外教的经验要求如何?
exp_demand = np.round(data_jlc['exp_title_clean'].value_counts()/data_jlc['exp_title_clean'].value_counts().sum()*100, 1)
bar = Bar("不同经验英语外教需求百分比(%)", width=500, height=500)
bar.add("", ['入门', '中高级', '管理', '主任', '实习'], exp_demand.values,  xaxis_label_textsize=20,
         yaxis_label_textsize=20, is_label_show=True)
bar.render()

哪些地区对洋外教的需求多?

area_demand = data_jlc['area'].value_counts().nlargest(11).drop('Others')
bar = Bar("对外教需求排名前10的城市", width = 600,height=500)
bar.add("", ['北京', '上海', '杭州', '深圳', '成都', '重庆', '广州', '武汉', '南京', '青岛'],
         area_demand.values,  xaxis_label_textsize=18, yaxis_label_textsize=20,
         is_label_show=True, label_color=['#130f40'])
bar.render()

salary_area = np.round(data_jlc.groupby('area')['salary_clean'].mean()[area_demand.index], 1)

# 中教对应城市的平均薪酬
city10 = ['北京', '上海', '杭州', '深圳', '成都', '重庆', '广州', '武汉', '南京', '青岛']
np.round(data_wx[data_wx['city'].isin(city10)].groupby('city')['salary_clean'].mean(), 1)
bar = Bar("对外教需求排名前10的城市的外教和中教平均工资", width = 800,height=500)
bar.add("外教", city10, salary_area.values,  xaxis_label_textsize=18, yaxis_label_textsize=20,
         is_label_show=True, legend_top=30)
bar.add("中教", city10, [9.8, 9.3, 8.9, 8.6, 8.9, 8.0, 8.0, 6.9, 8.0, 7.8],
        xaxis_label_textsize=18, yaxis_label_textsize=20, is_label_show=True, legend_top=30)
bar.render()

什么机构在招聘洋外教?

om_type_demand = data_jlc['com_type'].value_counts().nlargest(5)
bar = Bar("对外教需求排名前5的单位类型", width=600,height=500)
bar.add("", ['培训机构', '学校', '咨询机构', '其它', '外包机构'],
         com_type_demand.values,  xaxis_label_textsize=18, yaxis_label_textsize=20,
         is_label_show=True, label_color=['#130f40'])
bar.render()

np.round(data_jlc.loc[data_jlc['com_type'] =='Teaching Center', 'education'].value_counts()/562*100, 1)
salary_com_type = np.round(data_jlc.groupby('com_type')['salary_clean'].mean()[com_type_demand.index], 1)
bar = Bar("对外教需求排名前5的单位类型的外教平均工资", width = 600,height=500)
bar.add("", ['培训机构', '学校', '咨询机构', '其它', '外包机构'],
         salary_com_type.values,  xaxis_label_textsize=18, yaxis_label_textsize=20,
         is_label_show=True, label_color = ['#130f40'])
bar.render()

data_jlc[data_jlc['com_type']=='Teaching Center'].groupby('education')['com_type'].count()/562
company = data_jlc['company'].value_counts().nlargest(50)
wordcloud = WordCloud(width=1000, height=600)
wordcloud.add("", company.index, company.values, word_size_range=[20, 100])
wordcloud.render()

洋外教来源真的很乱吗?

sex = data_gm['Sex'].value_counts()
pie =Pie('性别比例',background_color='white', width=500, height=500)
pie.add('', ['男性', '女性', '未知'], sex.values,
         center=[50, 50], radius=[25, 50], rosetype='radius', is_label_show=True,
        legend_text_size=16, label_text_size=16)
pie.render()

data_gm['Province2'] = data_gm['Province'].str.extract(r"([a-z|A-Z]*\s?[a-z|A-Z]*)")
province = data_gm['Province2'].value_counts().drop('')
wordcloud = WordCloud(width=1000, height=600)
wordcloud.add("", province.index, province.values, word_size_range=[20, 60])
wordcloud.render()

知识总结

re库

pat_K = r"(.*)K-(.*)K"
low, high = re.findall(pattern=pat_K, string=data)[0]  # 根据之前设置的模式,找到所有符合条件的内容分别赋值为 low, high

pandas与numpy库

由下面众多知识点可知,数据分析的重要库就是 pandas 和 numpy

data_kdgd = pd.read_csv("幼儿园.csv", , encoding='ISO-8859-1')  # 是用 pandas 读取文件的函数,还可以读取 excel, hdf 等类型文件,直接将后面的 csv 改成 excel, hdf 即可。encoding 涉及到编码问题,以后为了方便起见,可以在读取时直接加上类似代码。
data_kdgd['type'] = '幼儿园'  # 在kdgd文件新建“type”列,里面集体赋值“幼儿园”。
data_wx = pd.concat([data_kdgd, data_pmsc, data_trn, data_clg])  # 是 pandas 用来合并不同表格的函数。
data_wx['area'].str.split("-", expand=True)  # 在一列中根据“-”分割
data_wx.loc[data_wx['province'] == '北京', 'city'] = '北京'  # 根据元素的选取条件来选取对应的数据集.
data_wx['exp'] = data_wx['exp'].map(exp_map)  # map 函数对指定序列做映射,即替换。
data_wx['degree'].unique()  # 去重+排序
np.round(data_wx['salary_clean'], 1)  # 返回某位浮点数

matplotlib库

g = sns.FacetGrid(data_salary, row="教师类型", height=4, aspect=2, xlim=(0, 30))  # seaborn 官方文档为英文,且只有一次用到,因此没有仔细查看用法,希望下次有机会再学习。
g.map(sns.distplot, "工资", rug=False)
plt.show()  # matplotlib 画图总是需要输入该函数

pyecharts库

attr = ['经验不限\nInternship', '一到三年\nEntry Level', '三到五年\nExecutive',
        '五到十年\nMid-Senior', '十年以上\nDirector']  # 横坐标名称
value1 = [10.1, 15.8, 14.0, 15.8, 20.9]  # attr 相对应得数据
value2 = [7.8, 8.0, 10.1, 13.6, 21.6]
bar1 = Bar("不同工作经验的英语外教与中教工资对比", width="700px",height= "500px" )  # 创建新图标,名字、宽度、长度
bar1.add("外教", attr, value1, xaxis_label_textsize=18, legend_top=30,
        yaxis_label_textsize=20, is_label_show=True)  # 添加标签及对应的数据和格式
bar1.add("中教", attr, value2, xaxis_label_textsize=18, legend_top=30,
        yaxis_label_textsize=20, is_label_show=True)
bar1.render()  # 在目录下创建一个html文件

## 如果需要在 jupyter lab 或者 notebook 中使用
# v0.5
bar  # 在 Cell 中,可以直接调用实例本身来显示图表,目前所有的类已经实现了 IPython Rich Display 的 _repr_html_ 方法
# v1.0 
# notebook
render_notebook()  # Jupyter Notebook 直接调用 render_notebook 随时随地渲染图表,默认为 Jupter-Notebook。
# jupyter lab
from pyecharts.globals import CurrentConfig, NotebookType
CurrentConfig.NOTEBOOK_TYPE = NotebookType.JUPYTER_LAB  # 声明
from pyecharts.echarts import Bar  # 与之前版本不同
bar.load_javascript()
bar.render_notebook()

完整代码

import re
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from pylab import mpl
from pyecharts import Bar
from pyecharts import WordCloud
from pyecharts import Pie

pd.set_option('display.max_columns', None)
pd.set_option('display.max_rows', None)

plt.style.use('ggplot')
mpl.rcParams['font.sans-serif'] = ['SimHei']  # 解决seaborn中文字体显示问题
plt.rc('figure', figsize=(10, 10))  # 把plt默认的图片size调大一点
# plt.rcParams["figure.dpi"] = mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题

# %matplotlib inline

data_kdgd = pd.read_csv("幼儿园.csv")
data_pmsc = pd.read_csv("中小学.csv")
data_trn = pd.read_csv("外语培训.csv")
data_clg = pd.read_csv("职业院校.csv")
# data_tic = pd.read_csv("teachinchina.csv")
data_jlc = pd.read_csv("jobleadchina.csv", encoding='ISO-8859-1')
data_gm = pd.read_csv("data_gm.csv")

# 把来自万行教师的四个数据集组合成一个Dataframe

data_kdgd['type'] = '幼儿园'
data_pmsc['type'] = '中小学'
data_trn['type'] = '外语培训'
data_clg['type'] = '职业院校'

data_wx = pd.concat([data_kdgd, data_pmsc, data_trn, data_clg])


# 万行教师数据清洗
#     清洗出省份、城市
#     经验、学历
#     工资

# 1. 清洗出省份、城市

data_wx['area'].str.split("-", expand=True)
data_wx['province'] = data_wx['area'].str.split("-", expand=True)[0]
data_wx['city'] = data_wx['area'].str.split("-", expand=True)[1]

# 把北京、天津、上海、重庆的城市改为原来的名字

data_wx.loc[data_wx['province'] == '北京', 'city'] = '北京'
data_wx.loc[data_wx['province'] == '上海', 'city'] = '上海'
data_wx.loc[data_wx['province'] == '天津', 'city'] = '天津'
data_wx.loc[data_wx['province'] == '重庆', 'city'] = '重庆'

# 2. 清洗出经验、学历

data_wx['exp'] = data_wx['exp_title'].str.split("/", expand=True)[0]
data_wx['degree'] = data_wx['exp_title'].str.split("/", expand=True)[1]
data_wx['exp'].unique()
exp_map = {'不限':'经验不限', '一年以上':'一到三年', '三年以上':'三到五年', '两年以上':'一到三年',
           '五年以上':'五到十年', '应届毕业生':'经验不限', '六年以上':'五到十年', '四年以上':'三到五年',
           '九年以上':'五到十年', '七年以上':'五到十年', '十年以上':'十年以上', '在读学生':'经验不限',
           '八年以上':'五到十年'}

data_wx['exp'] = data_wx['exp'].map(exp_map)
data_wx['degree'].unique()
degree_map = {'大专':'大专', '不限':'学历不限', '大学本科以上':'本科', '大学本科':'本科',
              '大专以上':'大专', '不限以上':'学历不限', '中专以上':'中专', '硕士以上':'硕士',
              '硕士':'硕士', '高中以上':'高中', '中专':'中专'}

data_wx['degree'] = data_wx['degree'].map(degree_map)
data_wx['salary'].unique()


def get_salary(data):
    pat_K = r"(.*)K-(.*)K"
    pat_W = r"(.*)W-(.*)W"
    pat = r"(.*)-(.*)/"
    if '面议' in data:
        return np.nan
    if '享公办教师薪资待遇' in data:
        return np.nan
    if 'K' in data and '月' in data:
        low, high = re.findall(pattern=pat_K, string=data)[0]
        return (float(low)+float(high))/2
    if 'W' in data and '年' in data:
        low, high = re.findall(pattern=pat_W, string=data)[0]
        return (float(low)+float(high))/2*10/12
    if 'K' not in data and '月' in data:
        low, high = re.findall(pattern=pat, string=data)[0]
        return (float(low)+float(high))/2/1000


data_wx['salary_clean'] = data_wx['salary'].apply(get_salary)
data_wx['salary_clean'] = np.round(data_wx['salary_clean'], 1)


# JobLeadChina数据清洗
#     清洗出“exp_title”
#     清洗出“salary”

# 1. 清洗出exp_title

data_jlc['exp_title'].unique()
data_jlc['exp_title_clean'] = data_jlc['exp_title'].str.split(": ", expand=True)[1]

# 2. 清洗出salary

data_jlc['salary'].unique()


def get_salary_jlc(data):
    pat_jlc = r"(.*)K/MTH - (.*)K/MTH"
    if '00' in data:
        low, high = re.findall(pattern=pat_jlc, string=data)[0]
        return (float(low)+float(high))/2/1000
    else:
        low, high = re.findall(pattern=pat_jlc, string=data)[0]
        return (float(low)+float(high))/2


data_jlc['salary_clean'] = data_jlc['salary'].apply(get_salary_jlc)


# 微信群成员数据清洗
data_gm = data_gm[['NickName', 'City', 'Province', 'Sex', 'Signature']]
data_gm.to_csv("data_gm.csv", index=False)

# 问题:
#
#     洋外教的工资真的高吗?
#     市场对于洋外教的经验和学历要求如何?
#     哪些地区对洋外教的需求多?
#     什么机构在招聘洋外教?
#     洋外教来源真的很乱吗?

# 1. 洋外教的工资真的高吗?

data_wx.drop(data_wx[data_wx['salary_clean'] > 40].index, inplace=True)

data_wx['teacher_type'] = '中教'
data_jlc['teacher_type'] = '外教'
data_salary = pd.concat([data_wx[['salary_clean', 'teacher_type']],
                        data_jlc[['salary_clean', 'teacher_type']]])

data_salary.rename(columns={'salary_clean': '工资', 'teacher_type': '教师类型'}, inplace=True)
# sns.set(font_scale=1.5)
g = sns.FacetGrid(data_salary, row="教师类型", height=4, aspect=2, xlim=(0, 30))
g.map(sns.distplot, "工资", rug=False)
plt.show()

np.round(data_jlc['salary_clean'].mean(), 1)
np.round(data_wx['salary_clean'].mean(), 1)
data_wx.groupby('city')['salary_clean'].mean().mean()
np.round(data_wx.groupby('city')['salary_clean'].mean().sort_values()[:10], 1)

# 通过经验进行对比

data_jlc.loc[data_jlc['exp_title_clean']=='Associate', 'exp_title_clean'] = 'Entry Level'
np.round(data_jlc.groupby('exp_title_clean')['salary_clean'].mean(), 1)
np.round(data_wx.groupby('exp')['salary_clean'].mean(), 1)


# attr = ['经验不限\nInternship', '一到三年\nEntry Level', '三到五年\nExecutive',
#         '五到十年\nMid-Senior', '十年以上\nDirector']
# value1 = [10.1, 15.8, 14.0, 15.8, 20.9]
# value2 = [7.8, 8.0, 10.1, 13.6, 21.6]
# bar1 = Bar("不同工作经验的英语外教与中教工资对比", width="700px",height= "500px" )
# bar1.add("外教", attr, value1, xaxis_label_textsize=18, legend_top=30,
#         yaxis_label_textsize=20, is_label_show=True)
# bar1.add("中教", attr, value2, xaxis_label_textsize=18, legend_top=30,
#         yaxis_label_textsize=20, is_label_show=True)
# bar1.render()
# exit()


# 通过学历进行对比

np.round(data_jlc.groupby('education')['salary_clean'].mean(), 1)
data_wx.loc[data_wx['degree'] == '中专', 'degree'] = '高中'
np.round(data_wx.groupby('degree')['salary_clean'].mean(), 1)

# attr = ['学历不限\nAny education', '高中', '大专\nAssociate',
#         '本科\nBachelor', '硕士\nMaster']
# value1 = [13.9, np.nan, 12.8, 16.3, 21.3]
# value2 = [7.5, 4.4, 6.4, 9.2, 11.0]
# bar = Bar("不同学历的英语外教与中教工资对比", width = 700,height=500)
# bar.add("外教", attr, value1, xaxis_label_textsize=15, legend_top=30,
#         yaxis_label_textsize=20, is_label_show=True)
# bar.add("中教", attr, value2, xaxis_label_textsize=15, legend_top=30,
#         yaxis_label_textsize=20, is_label_show=True)
# bar.render()
# exit()

# 2. 市场对于洋外教的经验和学历要求如何?

# exp_demand = np.round(data_jlc['exp_title_clean'].value_counts()/data_jlc['exp_title_clean'].value_counts().sum()*100, 1)
# bar = Bar("不同经验英语外教需求百分比(%)", width=500, height=500)
# bar.add("", ['入门', '中高级', '管理', '主任', '实习'], exp_demand.values,  xaxis_label_textsize=20,
#         yaxis_label_textsize=20, is_label_show=True)
# bar.render()
# exit()

# degree_demand = np.round(data_jlc['education'].value_counts()/data_jlc['education'].value_counts().sum()*100, 1)
# bar = Bar("不同学历外教需求百分比(%)", width=500, height=500)
# bar.add("", ['本科', '学历不限', '社区大学', '硕士'], degree_demand.values, xaxis_label_textsize=18,
#         yaxis_label_textsize=20, is_label_show=True)
# bar.render()

# 3. 哪些地区对洋外教的需求多?

area_demand = data_jlc['area'].value_counts().nlargest(11).drop('Others')
# bar = Bar("对外教需求排名前10的城市", width = 600,height=500)
# bar.add("", ['北京', '上海', '杭州', '深圳', '成都', '重庆', '广州', '武汉', '南京', '青岛'],
#         area_demand.values,  xaxis_label_textsize=18, yaxis_label_textsize=20,
#         is_label_show=True, label_color=['#130f40'])
# bar.render()
# exit()

salary_area = np.round(data_jlc.groupby('area')['salary_clean'].mean()[area_demand.index], 1)

# 中教对应城市的平均薪酬
# city10 = ['北京', '上海', '杭州', '深圳', '成都', '重庆', '广州', '武汉', '南京', '青岛']
# np.round(data_wx[data_wx['city'].isin(city10)].groupby('city')['salary_clean'].mean(), 1)
# bar = Bar("对外教需求排名前10的城市的外教和中教平均工资", width = 800,height=500)
# bar.add("外教", city10, salary_area.values,  xaxis_label_textsize=18, yaxis_label_textsize=20,
#         is_label_show=True, legend_top=30)
# bar.add("中教", city10, [9.8, 9.3, 8.9, 8.6, 8.9, 8.0, 8.0, 6.9, 8.0, 7.8],
#         xaxis_label_textsize=18, yaxis_label_textsize=20, is_label_show=True, legend_top=30)
# bar.render()
# exit()

#  4. 什么机构在招聘洋外教?
com_type_demand = data_jlc['com_type'].value_counts().nlargest(5)
# bar = Bar("对外教需求排名前5的单位类型", width=600,height=500)
# bar.add("", ['培训机构', '学校', '咨询机构', '其它', '外包机构'],
#         com_type_demand.values,  xaxis_label_textsize=18, yaxis_label_textsize=20,
#         is_label_show=True, label_color=['#130f40'])
# bar.render()
# exit()

np.round(data_jlc.loc[data_jlc['com_type'] =='Teaching Center', 'education'].value_counts()/562*100, 1)

salary_com_type = np.round(data_jlc.groupby('com_type')['salary_clean'].mean()[com_type_demand.index], 1)
# bar = Bar("对外教需求排名前5的单位类型的外教平均工资", width = 600,height=500)
# bar.add("", ['培训机构', '学校', '咨询机构', '其它', '外包机构'],
#         salary_com_type.values,  xaxis_label_textsize=18, yaxis_label_textsize=20,
#         is_label_show=True, label_color = ['#130f40'])
# bar.render()
# exit()

data_jlc[data_jlc['com_type']=='Teaching Center'].groupby('education')['com_type'].count()/562
company = data_jlc['company'].value_counts().nlargest(50)
# wordcloud = WordCloud(width=1000, height=600)
# wordcloud.add("", company.index, company.values, word_size_range=[20, 100])
# wordcloud.render()
# exit()

# 5. 洋外教来源真的很乱吗?

sex = data_gm['Sex'].value_counts()
# pie =Pie('性别比例',background_color='white', width=500, height=500)
# pie.add('', ['男性', '女性', '未知'], sex.values,
#         center=[50, 50], radius=[25, 50], rosetype='radius', is_label_show=True,
#        legend_text_size=16, label_text_size=16)
# pie.render()
# exit()

data_gm['Province2'] = data_gm['Province'].str.extract(r"([a-z|A-Z]*\s?[a-z|A-Z]*)")
province = data_gm['Province2'].value_counts().drop('')
wordcloud = WordCloud(width=1000, height=600)
wordcloud.add("", province.index, province.values, word_size_range=[20, 60])
wordcloud.render()
exit()

更多推荐

【编程】逐行解析之爬取分析外教薪资