用Pandas分析招聘数据

#明确分析目的和思路

#数据收集

本次练习用的是网络抓取的数据分析师的招聘薪资数据,点击【这里】即可下载。

#数据处理

#数据读取

原文件是 csv 格式的,pandas 有个专门读取 csv 文件的方法 read_csv可以用。当然如果是文本格式就用read_txt,Excel 格式就用read_excel,已经存入了数据库就用read_sql。还好是csv,一般其数据是干净的,如果碰到Excel的合并单元格就有得受了。

先导入 numpy 和 pandas 包,然后尝试读取:

1
2
3
4
5
import numpy as np
import pandas as pd

df = pd.read_csv('DataAnalyst.csv')
df.head()

输出的结果如下:

image-20220429170447077

首先,read_csv是默认用逗号分隔的,这里分隔正确,说明原文件是逗号分隔。而且 pandas 会智能识别字段名,由于第一行行首缺了一个数据,因此被正确识别为 columns 的名称了。我们可以看出数字和字母被正确识别了,但是潜在的文字却乱码了,故猜测原编码格式是汉字型的,需要用原编码打开。汉字编码首选的是gbk编码,我们用gbk试一下,给read_csv的关键字encoding传入gbk

1
df = pd.read_csv('DataAnalyst.csv', encoding='gbk')

这回有了:

image-20220429171002699

#数据清洗与整理

除了用head()方法检查数据外,可以用info()方法进行全局概览:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
In [3]: df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6876 entries, 0 to 6875
Data columns (total 17 columns):
 #   Column             Non-Null Count  Dtype 
---  ------             --------------  ----- 
 0   city               6876 non-null   object
 1   companyFullName    6876 non-null   object
 2   companyId          6876 non-null   int64 
 3   companyLabelList   6170 non-null   object
 4   companyShortName   6876 non-null   object
 5   companySize        6876 non-null   object
 6   businessZones      4873 non-null   object
 7   firstType          6869 non-null   object
 8   secondType         6870 non-null   object
 9   education          6876 non-null   object
 10  industryField      6876 non-null   object
 11  positionId         6876 non-null   int64 
 12  positionAdvantage  6876 non-null   object
 13  positionName       6876 non-null   object
 14  positionLables     6844 non-null   object
 15  salary             6876 non-null   object
 16  workYear           6876 non-null   object
dtypes: int64(2), object(15)
memory usage: 913.3+ KB

整个数据6876行,其中关键的(companyId、positionId、salary等)为不为null的都有6876,只有少数没那么关键的字段(companyLabelList,businessZones,secondType,positionLables)存在空缺。

这个关键要检查的是重复值。由于我们的目标是分析职位的,为此必须保证positionId唯一。我们以positionId不重复为核心,进行去重。

pandas.Series.unique()方法返回一个Series的唯一值的个数,包括NaN值。我们用它看看positionID是否有重复值:

1
2
In [4]: len(df.positionId.unique())
Out[4]: 5031

这个值小于6876,说明存在重复值,我们使用drop_duplicates清洗掉。

image-20220430101207996

这个drop_duplicates如果subset不指定某列会依据所有的列,因此这里需要指定为positionId。而keep默认值就是first,也就是如果发现重复值保存最前面的那个。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
In [10]: df_duplicates = df.drop_duplicates(subset='positionId')

In [11]: df_duplicates
Out[11]: 
     city    companyFullName  ...   salary workYear
0      上海     纽海信息技术(上海)有限公司  ...    7k-9k    应届毕业生
1      上海   上海点荣金融信息服务有限责任公司  ...  10k-15k    应届毕业生
2      上海     上海晶樵网络信息技术有限公司  ...    4k-6k    应届毕业生
3      上海  杭州数云信息技术有限公司上海分公司  ...    6k-8k    应届毕业生
4      上海     上海银基富力信息技术有限公司  ...    2k-3k    应届毕业生
...   ...                ...  ...      ...      ...
6054   北京   普信恒业科技发展北京有限公司  ...  15k-25k     3-5
6330   北京         普信资产管理有限公司  ...  15K-30K     3-5
6465   北京       北京三快在线科技有限公司  ...  30k-40k    5-10
6605   北京       北京富通基业投资有限公司  ...    4k-6k       不限
6766   北京   百度在线网络技术北京有限公司  ...  15k-30k       不限

[5031 rows x 17 columns]

清洗后只剩5031行。

以上字段都没什么问题,主要需要处理的还有薪资salary这列,它是一个范围,后期难以量化,因此我们需要提取上下限并计算平均值来代表该岗位的薪资。薪资这里既有小写k,也有大小K,还有「k以上」,k以上只能上下限默认相同。

要把它拆开,需要用到pandas中的apply。它可以针对DataFrame中的一行行数据进行操作,允许使用自定义函数。

这里,我们需要定义一个如下的函数,针对传递的参数不同,返回薪资的上下限。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
def cut_word(word, method):
    position = word.find('-')
    length = int(len(word))
    if position != -1:
        bottomSalary = word[:position-1]
        topSalary = word[position+1:length-1]
    else:
        bottomSalary = word[:word.upper().find('K')]
        topSalary = bottomSalary
    if method == 'top':
        return topSalary
    elif method == 'bottom':
        return bottomSalary
    else:
        return None

当我们传递的是'top',返回的是薪资上限,当我们传递的是'bottom',返回的是薪资下限。函数里,首先用字符串的方法寻找-的位置,如果没找到,会返回-1给position,就是属于xxk以上那种格式了,因此薪资上下限设定为相同,该值就是kK之前的那个数字。如果不等于-1,截取-之前kK之前的数字作为下限,截取-之后kK之前的数字作为上限。

注意,我这里在赋值前先拷贝了一份,防止pandas触发SettingWithCopyWarning警告,详情请看Returning a view versus a copy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
In [18]: df_dup = df_duplicates.copy()

In [19]: df_dup.loc[:,'bottomSalary'] = df_duplicates.loc[:,'salary'].apply(cut_
    ...: word, method='bottom').astype('int')

In [20]: df_dup.loc[:,'topSalary'] = df_duplicates.loc[:,'salary'].apply(cut_wor
    ...: d, method='top').astype('int')
  
In [21]: df_dup.loc[:,('topSalary', 'bottomSalary')]
Out[21]: 
      topSalary  bottomSalary
0             9             7
1            15            10
2             6             4
3             8             6
4             3             2
...         ...           ...
6054         25            15
6330         30            15
6465         40            30
6605          6             4
6766         30            15

[5031 rows x 2 columns]

没问题了,然后我们用个简单的lambda函数将这两列两两求平均,活的薪资的平均值。前面的lambda x:理解为输入,后面的星号区域则是针对输入的x进行运算。案例中,因为同时对top和bottom求平均值,所以需要加上x.bottomSalary和x.topSalary。word_cut的apply是针对Series,现在则是DataFrame。当然完全可以用(df_dup.bottomSalary + df_dup.topSalary)/2代替它。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
In [23]: df_d = df_dup.copy()
  
In [24]: df_d['avgSalary'] = df_dup.apply(lambda x: (x.bottomSalary+x.topSalary)
    ...: /2, axis=1)
  
In [25]: df_d.loc[:,('topSalary', 'bottomSalary', 'avgSalary')]
Out[25]: 
      topSalary  bottomSalary  avgSalary
0             9             7        8.0
1            15            10       12.5
2             6             4        5.0
3             8             6        7.0
4             3             2        2.5
...         ...           ...        ...
6054         25            15       20.0
6330         30            15       22.5
6465         40            30       35.0
6605          6             4        5.0
6766         30            15       22.5

注意,这里的apply需要指定下是沿着行计算的,因为apply默认是沿着纵向计算的。

我们按照相似的逻辑将另一个数值数据【companySize】的格式转换一下。

我们只需要将cut_word函数改造一下就行:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
def cut_word2(word, method):
    position = word.find('-')
    length = int(len(word))
    if position != -1:
        bottomNum = word[:position]
        topNum = word[position+1:length-1]
    else:
        bottomNum = word[:word.find('人')].strip('少于')
        topNum= bottomNum
    if method == 'top':
        return topNum
    elif method == 'bottom':
        return bottomNum
    else:
        return None

公司规模这里,有三种格式:

  1. xxx-yyy人
  2. xxx人以上
  3. 少于yyy人

为此,整体上还是先找-的位置,如果找到则提取上下限,如果找不到则找的位置,将前面的字符剔除带有的就行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

In [30]: df_dup.loc[:,'bottomNum'] = df_duplicates.loc[:,'companySize'].apply(cu
    ...: t_word2, method='bottom').astype('int')

In [31]: df_dup.loc[:,'topNum'] = df_duplicates.loc[:,'companySize'].apply(cut_w
    ...: ord2, method='top').astype('int')

In [32]: df_dup.loc[:,('bottomNum', 'topNum')]
Out[32]: 
      bottomNum  topNum
0          2000    2000
1           500    2000
2            50     150
3           150     500
4            15      50
...         ...     ...
6054       2000    2000
6330       2000    2000
6465       2000    2000
6605         50     150
6766       2000    2000

[5031 rows x 2 columns]

同理求平均,这里用了上面提及的另一种方式求:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
In [33]: df_d['avgCompanySize'] = (df_dup.bottomNum + df_dup.topNum)/2

In [34]: df_d.loc[:,('avgCompanySize')]
Out[34]: 
0       2000.0
1       1250.0
2        100.0
3        325.0
4         32.5
         ...  
6054    2000.0
6330    2000.0
6465    2000.0
6605     100.0
6766    2000.0
Name: avgCompanySize, Length: 5031, dtype: float64
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
In [45]: df_d.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 5031 entries, 0 to 6766
Data columns (total 21 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   city               5031 non-null   object 
 1   companyFullName    5031 non-null   object 
 2   companyId          5031 non-null   int64  
 3   companyLabelList   4529 non-null   object 
 4   companyShortName   5031 non-null   object 
 5   companySize        5031 non-null   object 
 6   businessZones      3535 non-null   object 
 7   firstType          5027 non-null   object 
 8   secondType         5028 non-null   object 
 9   education          5031 non-null   object 
 10  industryField      5031 non-null   object 
 11  positionId         5031 non-null   int64  
 12  positionAdvantage  5031 non-null   object 
 13  positionName       5031 non-null   object 
 14  positionLables     5007 non-null   object 
 15  salary             5031 non-null   object 
 16  workYear           5031 non-null   object 
 17  topSalary          5031 non-null   int64  
 18  bottomSalary       5031 non-null   int64  
 19  avgSalary          5031 non-null   float64
 20  avgCompanySize     5031 non-null   float64
dtypes: float64(2), int64(4), object(15)
memory usage: 864.7+ KB

至此,数据清洗与整理完成。选出我们想要的内容进行后续分析(大家可以选择自己需要的数据)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
In [46]: df_clean = df_d[['city','companyLabelList','companyShortName','companyS
    ...: ize','businessZones','firstType','secondType','education','industryFiel
    ...: d','positionAdvantage','positionName','positionLables','workYear','avgS
    ...: alary','avgCompanySize']]

In [47]: df_clean.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 5031 entries, 0 to 6766
Data columns (total 15 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   city               5031 non-null   object 
 1   companyLabelList   4529 non-null   object 
 2   companyShortName   5031 non-null   object 
 3   companySize        5031 non-null   object 
 4   businessZones      3535 non-null   object 
 5   firstType          5027 non-null   object 
 6   secondType         5028 non-null   object 
 7   education          5031 non-null   object 
 8   industryField      5031 non-null   object 
 9   positionAdvantage  5031 non-null   object 
 10  positionName       5031 non-null   object 
 11  positionLables     5007 non-null   object 
 12  workYear           5031 non-null   object 
 13  avgSalary          5031 non-null   float64
 14  avgCompanySize     5031 non-null   float64
dtypes: float64(2), object(13)
memory usage: 628.9+ KB

#数据分析与可视化

对于用pandas数据分析,一般分类数据用value_counts,数值数据用describe,这是最常用的两个统计方式。我们的字段中只有两个是数字型。

我们先来查看下各城市的岗位数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
In [54]: df_clean.city.value_counts()
Out[54]: 
北京    2347
上海     979
深圳     527
杭州     406
广州     335
成都     135
南京      83
武汉      69
西安      38
苏州      37
厦门      30
长沙      25
天津      20
Name: city, dtype: int64

数据中可以看到北京招募的数据分析师远多于其他城市,广州招聘的少于杭州。

我们依次分析数据分析师的学历要求,工作年限要求等。

薪资作为数值型,我们describe():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
In [55]: df_clean.describe()
Out[55]: 
         avgSalary  avgCompanySize
count  5031.000000     5031.000000
mean     17.111409      924.103558
std       8.996242      804.287198
min       1.500000       15.000000
25%      11.500000      100.000000
50%      15.000000      325.000000
75%      22.500000     2000.000000
max      75.000000     2000.000000

它能快速生成各类统计指标。数据分析师的薪资的平均数是17k,中位数是15k,两者相差不大,最大薪资在75k,应该是数据科学家或者数据分析总监档位的水平。标准差在8.99k,有一定的波动性,大部分分析师薪资在一个标准差,也就是8-25k之间。而公司规模,平均是924人,最小的也有15人的公司需要,最大的是2000人,标准差804这个很大,按标准差来看大部分公司是120-1728人,跨度很大,说明对数据分析师的需求并不太局限于公司规模的大小。

由于图表的直观性,数据分析的过程伴随着数据可视化,我们用图表说话吧。pandas自带绘图函数,它是以matplotlib包为基础封装,所以两者能够结合使用。

我们引入matplotlib并设定一个绘图风格:

1
2
3
In [64]: import matplotlib.pyplot as plt

In [65]: plt.style.use('ggplot')

如果用的是jupyter,可以加上一行写上%matplotlib inline,它是jupyter自带的方式,允许图表在cell中输出。

先做一个平均薪资直方图:

image-20220430152400322

如果不指定,默认是分为10组,我们这里分成了15组。

数据分布呈双峰状,因为原始数据来源于招聘网站的爬取,薪资很容易集中在某个区间,不是真实薪资的反应(10~20k的区间,以本文的计算公式,只会粗暴地落在15k,而非均匀分布)。

数据分析的一大思想是细分维度,现在观察不同城市、不同学历对薪资的影响。箱线图是最佳的观测方式。箱线图可以在一个图中表示很多个角度的信息。

1
df_clean.boxplot(column='avgSalary', by='city', figsize=(9, 7))

这里的by的意思是分组,根据city分组,显示avgSalary的数据信息:

image-20220430153520906

但结果报错了,未找到字体。因此需要我们手动指定字体,FontProperties用加载字体,设置一个载入中文字体的变量,不同系统的路径不一样:

1
2
from matplotlib.font_manager import FontProperties
font_zh = FontProperties(fname='/Library/Fonts/Arial Unicode.ttf')

ax.get_xticklabels获取坐标轴刻度,即无法正确显示城市名的白框,利用set_fontpeoperties更改字体。于是获得了我们想要的箱线图:

image-20220430153631903

上面箱线图的点是离群值,在这里就是比一般薪资大得多的薪资。横线是中位数。

从图中可以看出,北京的薪资是远高于其他城市的,尤其是中位数,上海和深圳稍次,然后是杭州,然后是广州、成都等。

image-20220430154556205

从学历上看,博士薪资遥遥领先,虽然在top区域不如本科和硕士,这点我们要后续分析。大专学历稍有弱势。

image-20220430154727904

从工作时间上看,其对薪资对影响很大,毕业生和工作多年的不在一个梯度。特别是10年以上的,薪资遥遥领先。

到目前为止,我们了解了城市、年限和学历对薪资的影响,但这些都是单一的变量,现在想知道北京和上海这两座城市,学历对薪资的影响。也就是城市+学历对薪资的影响。

image-20220430154957537

这里我们用isin方法对数据分析了筛选,相当于sql中的where语句。在by传递多个值,箱线图的刻度自动变成元组,也就达到了横向对比的作用,但这种方法并不适宜元素过多的场景。

从图上可以看到,不同学历背景下,北京都是稍优于上海的,北京愿意花费更多薪资吸引数据分析师,对硕士和博士更加明显,特别是在博士这个档次,跨越幅度非常大。

在pandas中,需要同时用到多个维度分析时,可以用groupby函数。它和SQL中的group by差不多,能将不同变量分组。它返回一个已经分组了的对象。

1
2
3
In [55]: df_clean.groupby('city')

In [55]: <pandas.core.groupby.generic.DataFrameGroupBy object at 0x7fc0f17dc5b0>

然后我们对它进行计算:

image-20220430155838448

它返回的是不同城市的各列计数结果,因为没有NaN,每列结果都是相等的。现在它和value_counts等价。

image-20220501082824703

换成mean,计算出了不同城市的平均薪资和平均公司规模。因为mean方法只针对数值,而各列中只有avgSalary和avgCompanySize是数值。

image-20220501083027064

groupby可以传递一组列表,这时得到一组层次化的Series。按城市和学历分组计算了平均薪资和平均公司规模。

image-20220501083140957

将堆叠的数据展开。在不同城市中,博士学历最高的薪资在深圳,硕士学历最高的薪资在杭州。北京综合薪资最好。这个分析结论有没有问题呢?不妨先看招聘人数。

image-20220501083424697

这次换成count,我们在groupby后面加一个avgSalary,说明只统计avgSalary的计数结果,不用混入相同数据。图上的结果很明确了,要求博士学历的岗位只有6个,所谓的平均薪资,也只取决于公司开出的价码,波动性很强,毕竟这只是招聘薪资,不代表真实的博士在职薪资。这也解释了上面几个图表的异常。

image-20220501083619510

这里使用了agg函数,同时传入count和mean方法,然后返回了不同公司的计数和平均值两个结果。所以前文的mean,count,其实都省略了agg。agg除了系统自带的几个函数,它也支持自定义函数。

image-20220501083703376

上图用lamba函数,返回了不同公司中最高薪资和最低薪资的差值。agg是一个很方便的函数,它能针对分组后的列数据进行丰富多彩的计算。但是在pandas的分组计算中,它也不是最灵活的函数。

现在我们有一个新的问题,我想计算出不同城市,招聘数据分析师需求前5的公司,应该如何处理?agg虽然能返回计数也能排序,但它返回的是所有结果,前五还需要手工计算。能不能直接返回前五结果?当然可以,这里再次请出apply。

image-20220501083951440

自定义了函数topN,将传入的数据计数,并且从大到小返回前五的数据。然后以city聚合分组,因为求的是前5的公司,所以对companyShortName调用topN函数。

同样的,如果我想知道不同城市,各职位招聘数前五,也能直接调用topN。

image-20220501084048668

agg和apply是不同的,虽然某些方法相近,比如求sum,count等,但是apply支持更细的粒度,它能按组进行复杂运算,将数据拆分合并,而agg则必须固定为列。

运用group by,我们已经能随意组合不同维度。接下来配合group by作图。

image-20220501122314144

多重聚合在作图上面没有太大差异,行列数据转置不要混淆即可。

image-20220501122708569

上述的图例我们都是用pandas封装过的方法作图,如果要进行更自由的可视化,直接调用matplotlib的函数会比较好,它和pandas及numpy是兼容的。plt已经在上文中调用并且命名

image-20220501123654219

上图将上海和北京的薪资数据以直方图的形式进行对比。因为北京和上海的分析师人数相差较远,所以无法直接对比,需要用density参数转化为密度。设置alpha透明度,它比箱线图更直观。

注意这个density参数,True即归一化。

image-20220501123547015

另外一种分析思路是对数据进行深加工。我们将薪资设立出不同的level

cut的作用是分桶,它也是数据分析常用的一种方法,将不同数据划分出不同等级,也就是将数值型数据加工成分类数据,在机器学习的特征工程中应用比较多。cut可以等距划分,传入一个数字就好。这里为了更好的区分,我传入了一组列表进行人工划分,加工成相应的标签。

这里有点像用excel制作直方图,先人工订好组宽。不过这里的组宽不是等距的。大概是作者根据经验定下的组距。

image-20220501135818797

下面也是进行绘制柱状图,不过跟前面的不太一样。是将数据转换成了百分比再进行绘制图。

下面的是运用group by ,组合了city和Level的维度,

另外,函数的 plot.bar( )里面的参数 stacked = True,指得是条状图叠加。

如果改为stacked=False的话,得出来的条状图就不会叠加的。

image-20220501140549172

用lambda转换百分比,然后作堆积百分比柱形图(matplotlib好像没有直接调用的函数)。当axis=1时,x.sum()指的是x所在行的总和。每行的百分比相加得1。这里可以较为清晰的看到不同等级在不同地区的薪资占比。它比箱线图和直方图的好处在于,通过人工划分,具备业务含义。0~3是实习生的价位,3~6是刚毕业没有基础的新人,整理数据那种,6~10是有一定基础的,以此类推。

制作词云图。

使用函数:

str.replace()

str.split()

dropna() - 该函数主要用于滤除缺失数据。

image-20220501141040985

现在的目的是统计数据分析师的标签。它只是看上去干净的数据,元素中的[ ]是无意义的,它是字符串的一部分,和数组没有关系。df_clean.positionLables是Series,并不能直接套用replace。apply是一个好方法,但是比较麻烦。这里需要str方法。

image-20220501141259462

采用str方法,str方法允许我们针对列中的元素,进行字符串相关的处理,这里的[1:-1]不再是DataFrame和Series的切片,而是对字符串截取,这里把[ ]都截取掉了。如果漏了str,就变成选取Series第二行至最后一行的数据,切记。

image-20220501141442767

使用完str后,它返回的仍旧是Series,当我们想要再次用replace去除空格。还是需要添加str的。现在的数据已经干净不少。

positionLables本身有空值,所以要删除,不然容易报错。再次用str.split方法,把元素中的标签按「,」拆分成列表。

image-20220501141722624

这里是重点,通过apply和value_counts函数统计标签数。因为各行元素已经转换成了列表,所以value_counts会逐行计算列表中的标签,apply的灵活性就在于此,它将value_counts应用在行上,最后将结果组成一张新表。

下面用unstack进行行列转换。

image-20220501142957891

看上去有点怪,因为它是统计所有标签在各个职位的出现次数,绝大多数肯定是NaN。

image-20220501143156540

将空值删除,并且重置为DataFrame,此时level_0为标签名,level_1为df_index的索引,也可以认为它对应着一个职位,0是该标签在职位中出现的次数,之前我没有命名,所以才会显示0。部分职位的标签可能出现多次,这里忽略它。

image-20220501143337421

用groupby计算出标签出现的次数。

制作云图,需要第三方包wordcloud.

anaconda没有,需要先pip install wordcloud。

image-20220501144558878

因为我是在jupyter中显示图片,所以需要额外的配置figsize,不然wide和height的配置无效。wordcloud也兼容pandas,所以直接将结果传入,然后显示图片,去除坐标。大功告成。

dataAnalyst

以上就是老师介绍的内容,我的好奇心驱使我还想研究更多的东西。

原字段有firstType和secondType,岗位类型的分布是这样的呢?因为city数据是全的,直接用city那列作为数量值进行排序,得到前20的岗位种类如下:

image-20220501171525379

绝大多数数据分析师岗位都隶属于【技术-后端开发】,运营和产品岗也不少。当然,第二种类是【数据分析】的第一种类也可能不同,例如【技术】、【设计】、【市场与营销】等。

如果大家不妨花些时间做下面的练习:

不同职位的词云图有没有差异?

不同薪资不同年限,他们岗位的标签词云会不会有差异?

不同薪资等级,和工作年限、职位的关系是怎么样的?

以上的代码,有没有更优化的实现方式?

薪资的上下限拆分,能不能用lambda方法?

到目前为止,我们进行的分析均是利用多维和可视化,多练习也就掌握了。其实它和excel没有多大区别,只是python能够更快地应付更多更复杂的数据,当然新人在开始的效率不会高。

#报告撰写