大家學(xué)習(xí)Python,不練習(xí)怎么行? 這次依舊使用網(wǎng)絡(luò)抓取的數(shù)據(jù)分析師的招聘薪資作為練習(xí)數(shù)據(jù)。在早期我們已經(jīng)用它進(jìn)行了Excel、BI可視化、SQL三方面的實(shí)戰(zhàn)訓(xùn)練,想必大家已經(jīng)很熟悉了,我不再贅述。 沒有下載過的同學(xué),后臺(tái)發(fā)送「練習(xí)數(shù)據(jù)」獲得鏈接。 主要內(nèi)容是進(jìn)行數(shù)據(jù)讀取,數(shù)據(jù)概述,數(shù)據(jù)清洗和整理,分析和可視化。按照本教程,相信大家的Pandas會(huì)上到一個(gè)新臺(tái)階,遇到文中沒有提及的錯(cuò)誤,通過搜索引擎解決吧。 首先載入我們的練習(xí)數(shù)據(jù)。 在pandas中,常用的載入函數(shù)是read_csv。除此之外還有read_excel和read_table,table可以讀取txt。若是服務(wù)器相關(guān)的部署,則還會(huì)用到read_sql,直接訪問數(shù)據(jù)庫(kù),但它必須配合mysql相關(guān)包。 read_csv擁有諸多的參數(shù),encoding是最常用的參數(shù)之一,它用來讀取csv格式的編碼。這里使用了gb2312,該編碼常見于windows,如果報(bào)錯(cuò),可以嘗試utf-8。 sep參數(shù)是分割符,有些csv文件用逗號(hào)分割列,有些是分號(hào),有些是\t,這些都需要具體設(shè)置。header參數(shù)為是否使用表頭作為列名,默認(rèn)是。names參數(shù)可以為列設(shè)置額外的名字,比如csv中的表頭是中文,但是在pandas中最好轉(zhuǎn)換成英文。 上述是主要的參數(shù),其他參數(shù)有興趣可以學(xué)習(xí)。一般來說,csv的數(shù)據(jù)都是干凈的,excel文件則有合并單元格這種惡心的玩意,盡量避免。 現(xiàn)在有了數(shù)據(jù)df,首先對(duì)數(shù)據(jù)進(jìn)行快速的瀏覽。 這里列舉出了數(shù)據(jù)集擁有的各類字段,一共有6876個(gè),其中companyLabelList,businessZones,secondType,positionLables都存在為空的情況。公司id和職位id為數(shù)字,其他都是字符串。 因?yàn)閿?shù)據(jù)集的數(shù)據(jù)比較多,如果我們只想瀏覽部分的話,可以使用head函數(shù),顯示頭部的數(shù)據(jù),默認(rèn)5,也可以自由設(shè)置參數(shù),如果是尾部數(shù)據(jù)則是tail。 數(shù)據(jù)集中,最主要的臟數(shù)據(jù)是薪資這塊,后續(xù)我們要拆成單獨(dú)的兩列。 看一下是否有重復(fù)的數(shù)據(jù)。 unique函數(shù)可以返回唯一值,數(shù)據(jù)集中positionId是職位ID,值唯一。配合len函數(shù)計(jì)算出唯一值共有5031個(gè),說明有多出來的重復(fù)值。 使用drop_duplicates清洗掉。 drop_duplicates函數(shù)通過subset參數(shù)選擇以哪個(gè)列為去重基準(zhǔn)。keep參數(shù)則是保留方式,first是保留第一個(gè),刪除后余重復(fù)值,last還是刪除前面,保留最后一個(gè)。duplicated函數(shù)功能類似,但它返回的是布爾值。 接下來加工salary薪資字段。目的是計(jì)算出薪資下限以及薪資上限。 薪資內(nèi)容沒有特殊的規(guī)律,既有小寫k,也有大小K,還有「k以上」這種蛋疼的用法,k以上只能上下限默認(rèn)相同。 這里需要用到pandas中的apply。它可以針對(duì)DataFrame中的一行或者一行數(shù)據(jù)進(jìn)行操作,允許使用自定義函數(shù)。 我們定義了個(gè)word_cut函數(shù),它查找「-」符號(hào)所在的位置,并且截取薪資范圍開頭至K之間的數(shù)字,也就是我們想要的薪資上限。apply將word_cut函數(shù)應(yīng)用在salary列的所有行。 「k以上」這類臟數(shù)據(jù)怎么辦呢?find函數(shù)會(huì)返回-1,如果按照原來的方式截取,是word[:-2],不是我們想要的結(jié)果,所以需要加一個(gè)if判斷。 因?yàn)閜ython大小寫敏感,我們用upper函數(shù)將k都轉(zhuǎn)換為K,然后以K作為截取。這里不建議用「以上」,因?yàn)橛胁糠峙K數(shù)據(jù)不包含這兩字。 將bottomSalary轉(zhuǎn)換為數(shù)字,如果轉(zhuǎn)換成功,說明所有的薪資數(shù)字都成功截取了。 薪資上限topSalary的思路也相近,只是變成截取后半部分。 在word_cout函數(shù)增加了新的參數(shù)用以判斷返回bottom還是top。apply中,參數(shù)是添加在函數(shù)后面,而不是里面的。這點(diǎn)需要注意。 接下來求解平均薪資。 數(shù)據(jù)類型轉(zhuǎn)換為數(shù)字,這里引入新的知識(shí)點(diǎn),匿名函數(shù)lamba。很多時(shí)候我們并不需要復(fù)雜地使用def定義函數(shù),而用lamdba作為一次性函數(shù)。 lambda x: ******* ,前面的lambda x:理解為輸入,后面的星號(hào)區(qū)域則是針對(duì)輸入的x進(jìn)行運(yùn)算。案例中,因?yàn)橥瑫r(shí)對(duì)top和bottom求平均值,所以需要加上x.bottomSalary和x.topSalary。word_cut的apply是針對(duì)Series,現(xiàn)在則是DataFrame。 axis是apply中的參數(shù),axis=1表示將函數(shù)用在行,axis=1則是列。 這里的lambda可以用(df_duplicates.bottomSalary + df_duplicates.topSalary)/2替代。 到此,數(shù)據(jù)清洗的部分完成。切選出我們想要的內(nèi)容進(jìn)行后續(xù)分析(大家可以選擇更多數(shù)據(jù))。 先對(duì)數(shù)據(jù)進(jìn)行幾個(gè)描述統(tǒng)計(jì)。 value_counts是計(jì)數(shù),統(tǒng)計(jì)所有非零元素的個(gè)數(shù),以降序的方式輸出Series。數(shù)據(jù)中可以看到北京招募的數(shù)據(jù)分析師一騎絕塵。 我們可以依次分析數(shù)據(jù)分析師的學(xué)歷要求,工作年限要求等。 針對(duì)數(shù)據(jù)分析師的薪資,我們用describe函數(shù)。 它能快速生成各類統(tǒng)計(jì)指標(biāo)。數(shù)據(jù)分析師的薪資的平均數(shù)是17k,中位數(shù)是15k,兩者相差不大,最大薪資在75k,應(yīng)該是數(shù)據(jù)科學(xué)家或者數(shù)據(jù)分析總監(jiān)檔位的水平。標(biāo)準(zhǔn)差在8.99k,有一定的波動(dòng)性,大部分分析師薪資在17+—9k之間。 一般分類數(shù)據(jù)用value_counts,數(shù)值數(shù)據(jù)用describe,這是最常用的兩個(gè)統(tǒng)計(jì)函數(shù)。 說了這么多文字,還是不夠直觀,我們用圖表說話。 pandas自帶繪圖函數(shù),它是以matplotlib包為基礎(chǔ)封裝,所以兩者能夠結(jié)合使用。 %matplotlib inline是jupyter自帶的方式,允許圖表在cell中輸出。plt.style.use('ggplot')使用R語言中的ggplot2配色作為繪圖風(fēng)格,純粹為了好看。 用hist函數(shù)很方便的就繪制除出直方圖,比excel快多了。圖表列出了數(shù)據(jù)分析師薪資的分布,因?yàn)榇蟛糠中劫Y集中20k以下,為了更細(xì)的粒度。將直方圖的寬距繼續(xù)縮小。 數(shù)據(jù)分布呈雙峰狀,因?yàn)樵紨?shù)據(jù)來源于招聘網(wǎng)站的爬取,薪資很容易集中在某個(gè)區(qū)間,不是真實(shí)薪資的反應(yīng)(10~20k的區(qū)間,以本文的計(jì)算公式,只會(huì)粗暴地落在15k,而非均勻分布)。 數(shù)據(jù)分析的一大思想是細(xì)分維度,現(xiàn)在觀察不同城市、不同學(xué)歷對(duì)薪資的影響。箱線圖是最佳的觀測(cè)方式。 圖表的標(biāo)簽出了問題,出現(xiàn)了白框,主要是圖表默認(rèn)用英文字體,而這里的都是中文,導(dǎo)致了沖突。所以需要改用matplotlib。 首先加載字體管理包,設(shè)置一個(gè)載入中文字體的變量,不同系統(tǒng)的路徑不一樣。boxplot是我們調(diào)用的箱線圖函數(shù),column選擇箱線圖的數(shù)值,by是選擇分類變量,figsize是尺寸。 ax.get_xticklabels獲取坐標(biāo)軸刻度,即無法正確顯示城市名的白框,利用set_fontpeoperties更改字體。于是獲得了我們想要的箱線圖。改變字體還有其他方法,大家可以網(wǎng)上搜索關(guān)鍵字「matplotlib 中文字體」,都有相應(yīng)教程。 從圖上我們看到,北京的數(shù)據(jù)分析師薪資高于其他城市,尤其是中位數(shù)。上海和深圳稍次,廣州甚至不如杭州。 從學(xué)歷看,博士薪資遙遙領(lǐng)先,雖然在top區(qū)域不如本科和碩士,這點(diǎn)我們要后續(xù)分析。大專學(xué)歷稍有弱勢(shì)。 工作年限看,薪資的差距進(jìn)一步拉大,畢業(yè)生和工作多年的不在一個(gè)梯度。雖然沒有其他行業(yè)的數(shù)據(jù)對(duì)比,但是可以確定,數(shù)據(jù)分析師的職場(chǎng)上升路線還是挺光明的。 到目前為止,我們了解了城市、年限和學(xué)歷對(duì)薪資的影響,但這些都是單一的變量,現(xiàn)在想知道北京和上海這兩座城市,學(xué)歷對(duì)薪資的影響。 在by傳遞多個(gè)值,箱線圖的刻度自動(dòng)變成元組,也就達(dá)到了橫向?qū)Ρ鹊淖饔茫ㄟ@方法其實(shí)并不好,以后會(huì)講解其他方式)。這種方法并不適宜元素過多的場(chǎng)景。從圖上可以看到,不同學(xué)歷背景下,北京都是稍優(yōu)于上海的,北京愿意花費(fèi)更多薪資吸引數(shù)據(jù)分析師,而在博士這個(gè)檔次,也是一個(gè)大幅度的跨越。我們不妨尋找其中的原因。 在pandas中,需要同時(shí)用到多個(gè)維度分析時(shí),可以用groupby函數(shù)。它和SQL中的group by差不多,能將不同變量分組。 上圖是標(biāo)準(zhǔn)的用法,按city列,針對(duì)不同城市進(jìn)行了分組。不過它并沒有返回分組后的結(jié)果,只返回了內(nèi)存地址。這時(shí)它只是一個(gè)對(duì)象,沒有進(jìn)行任何的計(jì)算,現(xiàn)在調(diào)用groupby的count方法。 它返回的是不同城市的各列計(jì)數(shù)結(jié)果,因?yàn)闆]有NaN,每列結(jié)果都是相等的?,F(xiàn)在它和value_counts等價(jià)。 換成mean,計(jì)算出了不同城市的平均薪資。因?yàn)閙ean方法只針對(duì)數(shù)值,而各列中只有avgSalary是數(shù)值,于是返回了這個(gè)唯一結(jié)果。 groupby可以傳遞一組列表,這時(shí)得到一組層次化的Series。按城市和學(xué)歷分組計(jì)算了平均薪資。 后面再調(diào)用unstack方法,進(jìn)行行列轉(zhuǎn)置,這樣看的就更清楚了。在不同城市中,博士學(xué)歷最高的薪資在深圳,碩士學(xué)歷最高的薪資在杭州。北京綜合薪資最好。這個(gè)分析結(jié)論有沒有問題呢?不妨先看招聘人數(shù)。 這次換成count,我們?cè)趃roupby后面加一個(gè)avgSalary,說明只統(tǒng)計(jì)avgSalary的計(jì)數(shù)結(jié)果,不用混入相同數(shù)據(jù)。圖上的結(jié)果很明確了,要求博士學(xué)歷的崗位只有6個(gè),所謂的平均薪資,也只取決于公司開出的價(jià)碼,波動(dòng)性很強(qiáng),畢竟這只是招聘薪資,不代表真實(shí)的博士在職薪資。這也解釋了上面幾個(gè)圖表的異常。 groupby是不是和數(shù)據(jù)透視表比較像?pandas其實(shí)有專門的數(shù)據(jù)透視函數(shù),在另外一方面,groupby確實(shí)能完成不少透視工作。 接下來計(jì)算不同公司招聘的數(shù)據(jù)分析師數(shù)量,并且計(jì)算平均數(shù)。 這里使用了agg函數(shù),同時(shí)傳入count和mean方法,然后返回了不同公司的計(jì)數(shù)和平均值兩個(gè)結(jié)果。所以前文的mean,count,其實(shí)都省略了agg。agg除了系統(tǒng)自帶的幾個(gè)函數(shù),它也支持自定義函數(shù)。
上圖用lamba函數(shù),返回了不同公司中最高薪資和最低薪資的差值。agg是一個(gè)很方便的函數(shù),它能針對(duì)分組后的列數(shù)據(jù)進(jìn)行豐富多彩的計(jì)算。但是在pandas的分組計(jì)算中,它也不是最靈活的函數(shù)。 現(xiàn)在我們有一個(gè)新的問題,我想計(jì)算出不同城市,招聘數(shù)據(jù)分析師需求前5的公司,應(yīng)該如何處理?agg雖然能返回計(jì)數(shù)也能排序,但它返回的是所有結(jié)果,前五還需要手工計(jì)算。能不能直接返回前五結(jié)果?當(dāng)然可以,這里再次請(qǐng)出apply。 自定義了函數(shù)topN,將傳入的數(shù)據(jù)計(jì)數(shù),并且從大到小返回前五的數(shù)據(jù)。然后以city聚合分組,因?yàn)榍蟮氖乔?的公司,所以對(duì)companyShortName調(diào)用topN函數(shù)。 同樣的,如果我想知道不同城市,各職位招聘數(shù)前五,也能直接調(diào)用topN。 可以看到,雖說是數(shù)據(jù)分析師,其實(shí)有不少的開發(fā)工程師,數(shù)據(jù)產(chǎn)品經(jīng)理等。這是抓取下來數(shù)據(jù)的缺點(diǎn),它反應(yīng)的是不止是數(shù)據(jù)分析師,而是數(shù)據(jù)領(lǐng)域。不同城市的需求不一樣,北京的數(shù)據(jù)產(chǎn)品經(jīng)理看上去要比上海高。 agg和apply是不同的,雖然某些方法相近,比如求sum,count等,但是apply支持更細(xì)的粒度,它能按組進(jìn)行復(fù)雜運(yùn)算,將數(shù)據(jù)拆分合并,而agg則必須固定為列。 運(yùn)用group by,我們已經(jīng)能隨意組合不同維度。接下來配合group by作圖。 多重聚合在作圖上面沒有太大差異,行列數(shù)據(jù)轉(zhuǎn)置不要混淆即可。 上述的圖例我們都是用pandas封裝過的方法作圖,如果要進(jìn)行更自由的可視化,直接調(diào)用matplotlib的函數(shù)會(huì)比較好,它和pandas及numpy是兼容的。plt已經(jīng)在上文中調(diào)用并且命名 上圖將上海和北京的薪資數(shù)據(jù)以直方圖的形式進(jìn)行對(duì)比。因?yàn)楸本┖蜕虾5姆治鰩熑藬?shù)相差較遠(yuǎn),所以無法直接對(duì)比,需要用normed參數(shù)轉(zhuǎn)化為密度。設(shè)置alpha透明度,它比箱線圖更直觀。 另外一種分析思路是對(duì)數(shù)據(jù)進(jìn)行深加工。我們將薪資設(shè)立出不同的level cut的作用是分桶,它也是數(shù)據(jù)分析常用的一種方法,將不同數(shù)據(jù)劃分出不同等級(jí),也就是將數(shù)值型數(shù)據(jù)加工成分類數(shù)據(jù),在機(jī)器學(xué)習(xí)的特征工程中應(yīng)用比較多。cut可以等距劃分,傳入一個(gè)數(shù)字就好。這里為了更好的區(qū)分,我傳入了一組列表進(jìn)行人工劃分,加工成相應(yīng)的標(biāo)簽。 用lambda轉(zhuǎn)換百分比,然后作堆積百分比柱形圖(matplotlib好像沒有直接調(diào)用的函數(shù))。這里可以較為清晰的看到不同等級(jí)在不同地區(qū)的薪資占比。它比箱線圖和直方圖的好處在于,通過人工劃分,具備業(yè)務(wù)含義。0~3是實(shí)習(xí)生的價(jià)位,3~6是剛畢業(yè)沒有基礎(chǔ)的新人,整理數(shù)據(jù)那種,6~10是有一定基礎(chǔ)的,以此類推。 現(xiàn)在只剩下最后一列數(shù)據(jù)沒有處理,標(biāo)簽數(shù)據(jù)。 現(xiàn)在的目的是統(tǒng)計(jì)數(shù)據(jù)分析師的標(biāo)簽。它只是看上去干凈的數(shù)據(jù),元素中的是無意義的,它是字符串的一部分,和數(shù)組沒有關(guān)系。 你可能會(huì)想到用replace這類函數(shù)。但是它并不能直接使用。df_clean.positionLables.replace會(huì)報(bào)錯(cuò),為什么呢?因?yàn)閐f_clean.positionLables是Series,并不能直接套用replace。apply是一個(gè)好方法,但是比較麻煩。 這里需要str方法。 str方法允許我們針對(duì)列中的元素,進(jìn)行字符串相關(guān)的處理,這里的[1:-1]不再是DataFrame和Series的切片,而是對(duì)字符串截取,這里把都截取掉了。如果漏了str,就變成選取Series第二行至最后一行的數(shù)據(jù),切記。 使用完str后,它返回的仍舊是Series,當(dāng)我們想要再次用replace去除空格。還是需要添加str的?,F(xiàn)在的數(shù)據(jù)已經(jīng)干凈不少。 positionLables本身有空值,所以要?jiǎng)h除,不然容易報(bào)錯(cuò)。再次用str.split方法,把元素中的標(biāo)簽按「,」拆分成列表。 這里是重點(diǎn),通過apply和value_counts函數(shù)統(tǒng)計(jì)標(biāo)簽數(shù)。因?yàn)楦餍性匾呀?jīng)轉(zhuǎn)換成了列表,所以value_counts會(huì)逐行計(jì)算列表中的標(biāo)簽,apply的靈活性就在于此,它將value_counts應(yīng)用在行上,最后將結(jié)果組成一張新表。 這里的運(yùn)算速度會(huì)有點(diǎn)慢,別擔(dān)心。 用unstack完成行列轉(zhuǎn)換,看上去有點(diǎn)怪,因?yàn)樗墙y(tǒng)計(jì)所有標(biāo)簽在各個(gè)職位的出現(xiàn)次數(shù),絕大多數(shù)肯定是NaN。 將空值刪除,并且重置為DataFrame,此時(shí)level_0為標(biāo)簽名,level_1為df_index的索引,也可以認(rèn)為它對(duì)應(yīng)著一個(gè)職位,0是該標(biāo)簽在職位中出現(xiàn)的次數(shù),之前我沒有命名,所以才會(huì)顯示0。部分職位的標(biāo)簽可能出現(xiàn)多次,這里忽略它。 最后用groupby計(jì)算出標(biāo)簽出現(xiàn)的次數(shù)。到這里,已經(jīng)計(jì)算出我們想要的結(jié)果。除了這種方法,也可以使用for循環(huán),大家可以試著練習(xí)一下,效率會(huì)慢不少。這種寫法的缺點(diǎn)是占用內(nèi)存較大,拿空間換時(shí)間,具體取舍看大家了。 加載wordcloud,anaconda沒有,自行下載吧。清洗掉引號(hào),設(shè)置詞云相關(guān)的參數(shù)。因?yàn)槲沂窃趈upyter中顯示圖片,所以需要額外的配置figsize,不然wide和height的配置無效。wordcloud也兼容pandas,所以直接將結(jié)果傳入,然后顯示圖片,去除坐標(biāo)。大功告成。 把最后結(jié)果的圖片右鍵另存為,發(fā)到朋友圈紀(jì)念一下吧,哈哈哈哈。 不同職位的詞云圖有沒有差異? 不同薪資不同年限,他們崗位的標(biāo)簽詞云會(huì)不會(huì)有差異? 不同薪資等級(jí),和工作年限、職位的關(guān)系是怎么樣的? 以上的代碼,有沒有更優(yōu)化的實(shí)現(xiàn)方式? 薪資的上下限拆分,能不能用lambda方法? 到目前為止,我們進(jìn)行的分析均是利用多維和可視化,多練習(xí)也就掌握了。其實(shí)它和excel沒有多大區(qū)別,只是python能夠更快地應(yīng)付更多更復(fù)雜的數(shù)據(jù),當(dāng)然新人在開始的效率不會(huì)高。 因?yàn)闀r(shí)間關(guān)系,更多的函數(shù)留到下次課程,下次課程也是最后一次了。 用戶贊賞用
|
|