美文网首页
案例-(旧)简易分析51job数据分析职位数据

案例-(旧)简易分析51job数据分析职位数据

作者: 花讽院_和狆 | 来源:发表于2020-03-11 10:25 被阅读0次

    初学python数据分析,就用51job的数据练练手,抓了大概7万左右51上全国的数据分析相关岗位的信息.

    首先要应用的就是numpy,pandas,matplotlib等几个库了.

    import numpy as np
    import pandas as pd
    from matplotlib import pyplot as plt
    from matplotlib import font_manager
    import re
    from typing import List, Tuple, Dict
    import math
    

    首先对数据进行简单处理,去掉空值,对列进行一个定义.

    #去掉空值
    df :pd.DataFrame = pd.read_csv(r'C:\\Users\\jjojjo\\Desktop\\51job.csv', encoding='utf-8', header=None, error_bad_lines=False)
    df.columns = ['name', 'salary', 'district', 'createtime', 'education', 'demand', 'corporation', 'type', 'scale', 'category']
    df1 :pd.DataFrame = df.dropna(axis=0, how='any', inplace=None, subset=['demand','corporation','district','salary'])
    

    一定要用utf-8的格式打开,否则会乱码!

    df.dropna中的subset参数是用来确定需要查几列的空值并drop掉,我这里让职位需求,公司名,地区,薪资这几列为空的都去掉.

    接下来要对工资进行一个解析,由于抓下来的薪资数据都是1-2万/月这种格式的,很难区分,因此这里用了个正则表达式来把数据进行拆分:

    #处理工资
    df2 = df1['salary'].str.extract(r'([年|月|小时|天]{1,})', expand=True)#不能直接引用列名来赋值,否则会报警,出现chained indexing
    df3 = df1['salary'].str.extract(r'([0-9]{1,}[.]*[0-9]*)-([0-9]{1,}[.]*[0-9]*)', expand=True)
    df4 = df1['salary'].str.extract(r'([百|千|万|元])', expand=True)
    df2.columns = ['salarytime']
    df1 = df1.join(df2)
    df3.columns = ['low', 'high']
    df1 = df1.join(df3)
    df4.columns = ['salaryunit']
    df1 = df1.join(df4)
    df1 = df1.dropna(axis=0, how='any', subset=['low'])
    
    df1['low'] = pd.to_numeric(df1['low'])
    df1['high'] = pd.to_numeric(df1['high'])#转换为数字
    
    def SalaryCal(x :str, y :float) -> float:
        if x == '年':
            return  y / 12
        elif x == '月':
            return y
        elif x == '千':
           return y * 1000
        elif x == '万':
            return y * 10000
    
    df1['low'] = df1.apply(lambda row: SalaryCal(row['salaryunit'], row['low']), axis=1)
    df1['high'] = df1.apply(lambda row: SalaryCal(row['salaryunit'], row['high']), axis=1)
    df1['low'] = df1.apply(lambda row: SalaryCal(row['salarytime'], row['low']), axis=1)
    df1['high'] = df1.apply(lambda row: SalaryCal(row['salarytime'], row['high']), axis=1)
    

    series.str.方法可以对str类型的列进行处理,非常方便,可以参考这个链接http://www.mamicode.com/info-detail-2331669.html,但是要注意不能用常用的str.findall来匹配正则,因为返回值是两个list,后期是没有办法处理的,这里用的extract,expand=True的意思则是把结果分成一个DataFrame,之后挨个进行合并..

    这是个笨办法,有时间再想想更好的!这里把薪资上限,下限,薪资单位都分离了出来,以便进行统一的计算.

    pd.to_numeric可以把对应列转换为数字,之前想了很多种办法,都容易出现chain index造成脚本跑不动,需要注意.

    然后定义的函数则是根据工资单位的不同统一计算工资,在这里直接用了apply函数,里面接一个lambda表达式,意思我理解为按照axis=1遍历df1,然后把每一个作为row参数到lambda表达式中进行运算,然后出来的series进行赋值,注意一定要加axis参数,否则会报错.

    接下来把地区进行分解,去掉有的区级地区,只保留省市,另外还对不那么相关的岗位进行了去重,但是这样的话就只有7000多个了....

    df1['district'] = df1['district'].str.split('-', expand=True)[0]#分解地区
    
    df1 = df1[df1['name'].str.contains('数据|分析')]
    
    df2 = df1.groupby(by='district').count()
    df2 = df2.sort_values(by='name', ascending=False)
    #df2 地区招收岗位数
    

    用series.str.contains可以判断列中是否包含对应的字符串.

    接下来再建立一个DataFrame,用来存储招收岗位数量前30的地区的薪资情况:

    df1['isin30'] = df1.apply(lambda x: x['district'] in df2.head(30).index, axis=1)
    df1 = df1[df1['isin30']]
    df3 = df1['low'].groupby(df1['district']).mean()
    df3 = df3.round(decimals=2)
    df3 = df3.sort_values(ascending=False)
    

    DataFrame.round()可以确定保留小数的位数,参数decimals为位数.

    然后再建立一个DataFrame用来画散点图,图中是每个地区的平均薪资:

    df4 = df2
    df4['meansalary'] = df3
    df4 = df4.dropna(axis=0, how='any', subset=['meansalary'])
    

    接下来开始画图,matplotlib用的不是很熟,感觉不如pyecharts顺手.

    my_font = font_manager.FontProperties(fname=r'C:\Windows\Fonts\msyh.ttc')
    

    设置中文,这个my_font很多地方都要用到(轴标签,刻度,图例,标题等)

    plt.figure(figsize=(19.2,10.8), dpi=100)
    grid = plt.GridSpec(3, 2, wspace=0.5, hspace=1)
    

    设置画布大小和坐标,figure是设置画布,figsize参数需要设置一个元组,里面放的是长宽,dpi则是画布比例,没有太深究,总之应该就是长宽*dpi等于屏幕分辨率的像素.

    GridSpec用来设置坐标,前两个参数代表列,之后设置列间距和行间距.

    接下来就可以按照每个坐标进行画图了,画图有两种方式,一种是直接用pyplot来做,一种是用轴来做,比较习惯plt吧.

    添加子图的方式就是先用subplot确定子图范围,然后接下来所有的操作就都在这个子图里了,直到声明下一个子图的范围.

    top20 = plt.subplot(grid[0,0])
    plt.title('岗位需求前10位',  fontproperties=my_font, fontsize=18, fontweight='bold')
    plt.xticks(range(len(df2.index)), df2.index, fontproperties=my_font, rotation=45)
    top20.set_ylim(df2.head(10)['name'].min() * 0.8, df2.head(10)['name'].max() * 1.2)
    # plt.yticks(range(len(df2.head(30)['name'])), df2.head(30)['name'], fontproperties=my_font, rotation=45)
    plt.ylabel('岗位数量', fontproperties=my_font,  fontweight='bold')
    recs :plt.bar = top20.bar(df2.head(10).index, df2.head(10)['name'], width=0.8)
    for rect in recs:
        height = rect.get_height()
        plt.text(rect.get_x() + rect.get_width() / 2, height + 1, str(height), ha='center', color='red')
    plt.legend(['岗位数量'], prop=my_font)
    
    #薪资水平前十位
    salary = plt.subplot(grid[0,1])
    plt.title('薪资水平情况',  fontproperties=my_font, fontsize=18, fontweight='bold')
    plt.xticks(range(len(df3.index)), df3.index, fontproperties=my_font, rotation=45)
    salary.set_ylim(df3.min() * 0.8, df3.max() * 1.2)
    plt.ylabel('薪资水平', fontproperties=my_font,  fontweight='bold')
    recs :plt.bar = salary.bar(df3.head(10).index, df3.head(10), width=0.8)
    for rect in recs:
        height = rect.get_height()
        plt.text(rect.get_x() + rect.get_width() / 2, height + 1, str(height), ha='center', color='red', size=8, rotation=45)
    plt.legend(['薪资水平'], prop=my_font)
    

    画图比较粗糙,好在可以一边写一边看一边调整,很多参数硬记就可以了,比如定义字体的参数,有的用fontproperties,有的用prop,很乱.

    象限图需要说明一下,好像matplotlib里是没有象限图的,就用散点图+坐标轴移动的方式来实现.

    f.set_xlim(df4['name'].min()*0.95, df4['name'].max()*1.05)
    f.set_ylim(df4['meansalary'].min()*0.95, df4['meansalary'].max()*1.05)#定义x,y轴最大值最小值
    f.spines['top'].set_color('none')#去掉顶层边框
    f.spines['right'].set_color('none')#去掉右侧边框
    f.xaxis.set_ticks_position('bottom')#设置底边框
    f.spines['bottom'].set_position(('data', df4['meansalary'].median()))#x轴底边框设置在中位数附近
    f.yaxis.set_ticks_position('left')#设置左边框
    f.spines['left'].set_position(('data', df4['name'].quantile(q=0.85)))#左边框位置设置在0.85附近
    

    移动坐标轴的原理就是先定义轴坐标,这里定为最小值的0.95-最大值的1.05这个范围,然后把上边框和右边框隐藏掉,再定义出左边框和底边框,然后设置他们的位置就可以了.

    轴坐标是固定的,但是边框是可变的,这点记住就可以了.

    剩下就是画散点图,加标签等等,刻度隐藏掉,用plt.text加上文字,其中前两个参数是横纵坐标,坐标是像素,需要注意.

    然后用plt.annotate给每个散点加上坐标.

    f.set_xticks([])
    f.set_yticks([])
    plt.text(1226,6712, '薪资水平',fontproperties=my_font,size=8,alpha=0.5,ha='left')
    plt.text(310,11792, '岗位数量',fontproperties=my_font,size=8,alpha=0.5,ha='left')
    s = f.scatter(df4['name'], df4['meansalary'],alpha=0.8)
    for i in range(len(df4['name'])):
        plt.annotate(df4.index[i], xy=(df4['name'].iloc[i], df4['meansalary'].iloc[i]), xytext=(df4['name'].iloc[i]+0.1, df4['meansalary'].iloc[i]+0.1), fontproperties=my_font)
    
    

    最后出来的效果就是这样的:

    image

    可以看到,在51JOB上,上海的岗位需求远超其他地区,而且薪资相对较高,可能是一个数据分析方向比较好的去处,北京需求少一些,但是整体薪资比较高,大量的二线城市还是处在需求和待遇都比较低的层次上,不是很乐观.

    另外这里为了转换成实际情况,薪资取的都是下限,如果是上限可能还会好看一点儿.

    行业分类和岗位需求还没有做,过后有机会再实施一下.

    相关文章

      网友评论

          本文标题:案例-(旧)简易分析51job数据分析职位数据

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