初學python數據分析,就用51job的數據練練手,抓了大概7萬左右51上全國的數據分析相關崗位的信息.html
首先要應用的就是numpy,pandas,matplotlib等幾個庫了.python
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的格式打開,不然會亂碼!app
df.dropna中的subset參數是用來肯定須要查幾列的空值並drop掉,我這裏讓職位需求,公司名,地區,薪資這幾列爲空的都去掉.echarts
接下來要對工資進行一個解析,因爲抓下來的薪資數據都是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,以後挨個進行合併..字體
這是個笨辦法,有時間再想一想更好的!這裏把薪資上限,下限,薪資單位都分離了出來,以便進行統一的計算.spa
pd.to_numeric能夠把對應列轉換爲數字,以前想了不少種辦法,都容易出現chain index形成腳本跑不動,須要注意.code
而後定義的函數則是根據工資單位的不一樣統一計算工資,在這裏直接用了apply函數,裏面接一個lambda表達式,意思我理解爲按照axis=1遍歷df1,而後把每個做爲row參數到lambda表達式中進行運算,而後出來的series進行賦值,注意必定要加axis參數,不然會報錯.htm
接下來把地區進行分解,去掉有的區級地區,只保留省市,另外還對不那麼相關的崗位進行了去重,可是這樣的話就只有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)
最後出來的效果就是這樣的:
能夠看到,在51JOB上,上海的崗位需求遠超其餘地區,並且薪資相對較高,多是一個數據分析方向比較好的去處,北京需求少一些,可是總體薪資比較高,大量的二線城市仍是處在需求和待遇都比較低的層次上,不是很樂觀.
另外這裏爲了轉換成實際狀況,薪資取的都是下限,若是是上限可能還會好看一點兒.
行業分類和崗位需求尚未作,事後有機會再實施一下.