如何七周成為數據分析師22:用pandas進行數據分析實戰
本文是《如何七周成為數據分析師》的第二十二篇教程,如果想要了解寫作初衷,可以先行閱讀七周指南。溫馨提示:如果您已經熟悉Python,大可不必再看這篇文章,或只挑選部分。
大家學習Python,不練習怎么行?
這次依舊使用網絡抓取的數據分析師的招聘薪資作為練習數據。在早期我們已經用它進行了Excel、BI可視化、SQL三方面的實戰訓練,想必大家已經很熟悉了,我不再贅述。
主要內容是進行數據讀取,數據概述,數據清洗和整理,分析和可視化。按照本教程,相信大家的Pandas會上到一個新臺階,遇到文中沒有提及的錯誤,通過搜索引擎解決吧。
首先載入我們的練習數據。
在pandas中,常用的載入函數是read_csv。除此之外還有read_excel和read_table,table可以讀取txt。若是服務器相關的部署,則還會用到read_sql,直接訪問數據庫,但它必須配合mysql相關包。
read_csv擁有諸多的參數,encoding是最常用的參數之一,它用來讀取csv格式的編碼。這里使用了gb2312,該編碼常見于windows,如果報錯,可以嘗試utf-8。
sep參數是分割符,有些csv文件用逗號分割列,有些是分號,有些是\t,這些都需要具體設置。header參數為是否使用表頭作為列名,默認是。names參數可以為列設置額外的名字,比如csv中的表頭是中文,但是在pandas中最好轉換成英文。
上述是主要的參數,其他參數有興趣可以學習。一般來說,csv的數據都是干凈的,excel文件則有合并單元格這種惡心的玩意,盡量避免。
現在有了數據df,首先對數據進行快速的瀏覽。
這里列舉出了數據集擁有的各類字段,一共有6876個,其中companyLabelList,businessZones,secondType,positionLables都存在為空的情況。公司id和職位id為數字,其他都是字符串。
因為數據集的數據比較多,如果我們只想瀏覽部分的話,可以使用head函數,顯示頭部的數據,默認5,也可以自由設置參數,如果是尾部數據則是tail。
數據集中,最主要的臟數據是薪資這塊,后續我們要拆成單獨的兩列。
看一下是否有重復的數據。
unique函數可以返回唯一值,數據集中positionId是職位ID,值唯一。配合len函數計算出唯一值共有5031個,說明有多出來的重復值。
使用drop_duplicates清洗掉。
drop_duplicates函數通過subset參數選擇以哪個列為去重基準。keep參數則是保留方式,first是保留第一個,刪除后余重復值,last還是刪除前面,保留最后一個。duplicated函數功能類似,但它返回的是布爾值。
接下來加工salary薪資字段。目的是計算出薪資下限以及薪資上限。
薪資內容沒有特殊的規律,既有小寫k,也有大小K,還有「k以上」這種蛋疼的用法,k以上只能上下限默認相同。
這里需要用到pandas中的apply。它可以針對DataFrame中的一行或者一行數據進行操作,允許使用自定義函數。
我們定義了個word_cut函數,它查找「-」符號所在的位置,并且截取薪資范圍開頭至K之間的數字,也就是我們想要的薪資上限。apply將word_cut函數應用在salary列的所有行。
「k以上」這類臟數據怎么辦呢?find函數會返回-1,如果按照原來的方式截取,是word[:-2],不是我們想要的結果,所以需要加一個if判斷。
因為python大小寫敏感,我們用upper函數將k都轉換為K,然后以K作為截取。這里不建議用「以上」,因為有部分臟數據不包含這兩字。
將bottomSalary轉換為數字,如果轉換成功,說明所有的薪資數字都成功截取了。
薪資上限topSalary的思路也相近,只是變成截取后半部分。
在word_cout函數增加了新的參數用以判斷返回bottom還是top。apply中,參數是添加在函數后面,而不是里面的。這點需要注意。
接下來求解平均薪資。
數據類型轉換為數字,這里引入新的知識點,匿名函數lamba。很多時候我們并不需要復雜地使用def定義函數,而用lamdba作為一次性函數。
lambda x: ******* ,前面的lambda x:理解為輸入,后面的星號區域則是針對輸入的x進行運算。案例中,因為同時對top和bottom求平均值,所以需要加上x.bottomSalary和x.topSalary。word_cut的apply是針對Series,現在則是DataFrame。
axis是apply中的參數,axis=1表示將函數用在行,axis=1則是列。
這里的lambda可以用(df_duplicates.bottomSalary +?df_duplicates.topSalary)/2替代。
到此,數據清洗的部分完成。切選出我們想要的內容進行后續分析(大家可以選擇更多數據)。
先對數據進行幾個描述統計。
value_counts是計數,統計所有非零元素的個數,以降序的方式輸出Series。數據中可以看到北京招募的數據分析師一騎絕塵。
我們可以依次分析數據分析師的學歷要求,工作年限要求等。
針對數據分析師的薪資,我們用describe函數。
它能快速生成各類統計指標。數據分析師的薪資的平均數是17k,中位數是15k,兩者相差不大,最大薪資在75k,應該是數據科學家或者數據分析總監檔位的水平。標準差在8.99k,有一定的波動性,大部分分析師薪資在17+—9k之間。
一般分類數據用value_counts,數值數據用describe,這是最常用的兩個統計函數。
說了這么多文字,還是不夠直觀,我們用圖表說話。
pandas自帶繪圖函數,它是以matplotlib包為基礎封裝,所以兩者能夠結合使用。
%matplotlib inline是jupyter自帶的方式,允許圖表在cell中輸出。plt.style.use(‘ggplot’)使用R語言中的ggplot2配色作為繪圖風格,純粹為了好看。
用hist函數很方便的就繪制除出直方圖,比excel快多了。圖表列出了數據分析師薪資的分布,因為大部分薪資集中20k以下,為了更細的粒度。將直方圖的寬距繼續縮小。
數據分布呈雙峰狀,因為原始數據來源于招聘網站的爬取,薪資很容易集中在某個區間,不是真實薪資的反應(10~20k的區間,以本文的計算公式,只會粗暴地落在15k,而非均勻分布)。
數據分析的一大思想是細分維度,現在觀察不同城市、不同學歷對薪資的影響。箱線圖是最佳的觀測方式。
圖表的標簽出了問題,出現了白框,主要是圖表默認用英文字體,而這里的都是中文,導致了沖突。所以需要改用matplotlib。
首先加載字體管理包,設置一個載入中文字體的變量,不同系統的路徑不一樣。boxplot是我們調用的箱線圖函數,column選擇箱線圖的數值,by是選擇分類變量,figsize是尺寸。
ax.get_xticklabels獲取坐標軸刻度,即無法正確顯示城市名的白框,利用set_fontpeoperties更改字體。于是獲得了我們想要的箱線圖。改變字體還有其他方法,大家可以網上搜索關鍵字「matplotlib 中文字體」,都有相應教程。
從圖上我們看到,北京的數據分析師薪資高于其他城市,尤其是中位數。上海和深圳稍次,廣州甚至不如杭州。
從學歷看,博士薪資遙遙領先,雖然在top區域不如本科和碩士,這點我們要后續分析。大專學歷稍有弱勢。
工作年限看,薪資的差距進一步拉大,畢業生和工作多年的不在一個梯度。雖然沒有其他行業的數據對比,但是可以確定,數據分析師的職場上升路線還是挺光明的。
到目前為止,我們了解了城市、年限和學歷對薪資的影響,但這些都是單一的變量,現在想知道北京和上海這兩座城市,學歷對薪資的影響。
在by傳遞多個值,箱線圖的刻度自動變成元組,也就達到了橫向對比的作用(這方法其實并不好,以后會講解其他方式)。這種方法并不適宜元素過多的場景。從圖上可以看到,不同學歷背景下,北京都是稍優于上海的,北京愿意花費更多薪資吸引數據分析師,而在博士這個檔次,也是一個大幅度的跨越。我們不妨尋找其中的原因。
在pandas中,需要同時用到多個維度分析時,可以用groupby函數。它和SQL中的group by差不多,能將不同變量分組。
上圖是標準的用法,按city列,針對不同城市進行了分組。不過它并沒有返回分組后的結果,只返回了內存地址。這時它只是一個對象,沒有進行任何的計算,現在調用groupby的count方法。
它返回的是不同城市的各列計數結果,因為沒有NaN,每列結果都是相等的?,F在它和value_counts等價。
換成mean,計算出了不同城市的平均薪資。因為mean方法只針對數值,而各列中只有avgSalary是數值,于是返回了這個唯一結果。
groupby可以傳遞一組列表,這時得到一組層次化的Series。按城市和學歷分組計算了平均薪資。
后面再調用unstack方法,進行行列轉置,這樣看的就更清楚了。在不同城市中,博士學歷最高的薪資在深圳,碩士學歷最高的薪資在杭州。北京綜合薪資最好。這個分析結論有沒有問題呢?不妨先看招聘人數。
這次換成count,我們在groupby后面加一個avgSalary,說明只統計avgSalary的計數結果,不用混入相同數據。圖上的結果很明確了,要求博士學歷的崗位只有6個,所謂的平均薪資,也只取決于公司開出的價碼,波動性很強,畢竟這只是招聘薪資,不代表真實的博士在職薪資。這也解釋了上面幾個圖表的異常。
groupby是不是和數據透視表比較像?pandas其實有專門的數據透視函數,在另外一方面,groupby確實能完成不少透視工作。
接下來計算不同公司招聘的數據分析師數量,并且計算平均數。
這里使用了agg函數,同時傳入count和mean方法,然后返回了不同公司的計數和平均值兩個結果。所以前文的mean,count,其實都省略了agg。agg除了系統自帶的幾個函數,它也支持自定義函數。
上圖用lamba函數,返回了不同公司中最高薪資和最低薪資的差值。agg是一個很方便的函數,它能針對分組后的列數據進行豐富多彩的計算。但是在pandas的分組計算中,它也不是最靈活的函數。
現在我們有一個新的問題,我想計算出不同城市,招聘數據分析師需求前5的公司,應該如何處理?agg雖然能返回計數也能排序,但它返回的是所有結果,前五還需要手工計算。能不能直接返回前五結果?當然可以,這里再次請出apply。
自定義了函數topN,將傳入的數據計數,并且從大到小返回前五的數據。然后以city聚合分組,因為求的是前5的公司,所以對companyShortName調用topN函數。
同樣的,如果我想知道不同城市,各職位招聘數前五,也能直接調用topN。
可以看到,雖說是數據分析師,其實有不少的開發工程師,數據產品經理等。這是抓取下來數據的缺點,它反應的是不止是數據分析師,而是數據領域。不同城市的需求不一樣,北京的數據產品經理看上去要比上海高。
agg和apply是不同的,雖然某些方法相近,比如求sum,count等,但是apply支持更細的粒度,它能按組進行復雜運算,將數據拆分合并,而agg則必須固定為列。
運用group by,我們已經能隨意組合不同維度。接下來配合group by作圖。
多重聚合在作圖上面沒有太大差異,行列數據轉置不要混淆即可。
上述的圖例我們都是用pandas封裝過的方法作圖,如果要進行更自由的可視化,直接調用matplotlib的函數會比較好,它和pandas及numpy是兼容的。plt已經在上文中調用并且命名
上圖將上海和北京的薪資數據以直方圖的形式進行對比。因為北京和上海的分析師人數相差較遠,所以無法直接對比,需要用normed參數轉化為密度。設置alpha透明度,它比箱線圖更直觀。
另外一種分析思路是對數據進行深加工。我們將薪資設立出不同的level
cut的作用是分桶,它也是數據分析常用的一種方法,將不同數據劃分出不同等級,也就是將數值型數據加工成分類數據,在機器學習的特征工程中應用比較多。cut可以等距劃分,傳入一個數字就好。這里為了更好的區分,我傳入了一組列表進行人工劃分,加工成相應的標簽。
用lambda轉換百分比,然后作堆積百分比柱形圖(matplotlib好像沒有直接調用的函數)。這里可以較為清晰的看到不同等級在不同地區的薪資占比。它比箱線圖和直方圖的好處在于,通過人工劃分,具備業務含義。0~3是實習生的價位,3~6是剛畢業沒有基礎的新人,整理數據那種,6~10是有一定基礎的,以此類推。
現在只剩下最后一列數據沒有處理,標簽數據。
現在的目的是統計數據分析師的標簽。它只是看上去干凈的數據,元素中的[]是無意義的,它是字符串的一部分,和數組沒有關系。
你可能會想到用replace這類函數。但是它并不能直接使用。df_clean.positionLables.replace會報錯,為什么呢?因為df_clean.positionLables是Series,并不能直接套用replace。apply是一個好方法,但是比較麻煩。
這里需要str方法。
str方法允許我們針對列中的元素,進行字符串相關的處理,這里的[1:-1]不再是DataFrame和Series的切片,而是對字符串截取,這里把[]都截取掉了。如果漏了str,就變成選取Series第二行至最后一行的數據,切記。
使用完str后,它返回的仍舊是Series,當我們想要再次用replace去除空格。還是需要添加str的?,F在的數據已經干凈不少。
positionLables本身有空值,所以要刪除,不然容易報錯。再次用str.split方法,把元素中的標簽按「,」拆分成列表。
這里是重點,通過apply和value_counts函數統計標簽數。因為各行元素已經轉換成了列表,所以value_counts會逐行計算列表中的標簽,apply的靈活性就在于此,它將value_counts應用在行上,最后將結果組成一張新表。
這里的運算速度會有點慢,別擔心
用unstack完成行列轉換,看上去有點怪,因為它是統計所有標簽在各個職位的出現次數,絕大多數肯定是NaN。
將空值刪除,并且重置為DataFrame,此時level_0為標簽名,level_1為df_index的索引,也可以認為它對應著一個職位,0是該標簽在職位中出現的次數,之前我沒有命名,所以才會顯示0。部分職位的標簽可能出現多次,這里忽略它。
最后用groupby計算出標簽出現的次數。到這里,已經計算出我們想要的結果。除了這種方法,也可以使用for循環,大家可以試著練習一下,效率會慢不少。這種寫法的缺點是占用內存較大,拿空間換時間,具體取舍看大家了。
加載wordcloud,anaconda沒有,自行下載吧。清洗掉引號,設置詞云相關的參數。因為我是在jupyter中顯示圖片,所以需要額外的配置figsize,不然wide和height的配置無效。wordcloud也兼容pandas,所以直接將結果傳入,然后顯示圖片,去除坐標。大功告成。
把最后結果的圖片右鍵另存為,發到朋友圈紀念一下吧,哈哈哈哈。
如果大家不妨花些時間做下面的練習:
- 不同職位的詞云圖有沒有差異?
- 不同薪資不同年限,他們崗位的標簽詞云會不會有差異?
- 不同薪資等級,和工作年限、職位的關系是怎么樣的?
- 以上的代碼,有沒有更優化的實現方式?
- 薪資的上下限拆分,能不能用lambda方法?
到目前為止,我們進行的分析均是利用多維和可視化,多練習也就掌握了。其實它和excel沒有多大區別,只是python能夠更快地應付更多更復雜的數據,當然新人在開始的效率不會高。
因為時間關系,更多的函數留到下次課程,下次課程也是最后一次了。
相關閱讀
如何七周成為數據分析師01:常見的Excel函數全部涵蓋在這里了
如何七周成為數據分析師:Excel技巧之甘特圖繪制(項目管理)
如何七周成為數據分析師21:Python分析之numpy和pandas入門
#專欄作家#
秦路,微信公眾號ID:tracykanc,人人都是產品經理專欄作家。
本文由 @秦路?原創發布于人人都是產品經理。未經許可,禁止轉載。
老師,麻煩可以發一下練習數據嘛,找了您的公眾號也沒有的
文中的聯系數據從哪里下載呀
敲到上面第一段程序,大家應該會有個疑問,pd.read_csv()中的filepath(文件目錄)怎么調用,其實可以先定義個變量再引用。
例:
>>>filep=’D:\\文檔\\BI練習\\BI\\DataAnalyst.csv’ #雙斜杠為第一個’\’是作為轉義符號,第二個’\’才是目錄中真正的\
>>> df=pd.read_csv(filep,encoding=’gb2312′)
>>>df
看不到圖片的同學可以在網頁按F12,在上方菜單點擊Console,后在可以輸入的地方輸入我寫得這段代碼重新加載圖片:
代碼:
$(“p”).find(“img”).each(function(){
var srcs=$(this).attr(“src”);
$(this).attr(“src”,srcs.split(“!”)[0])
});
復制粘貼后請把這網站自動轉換掉的中文符號例如:單引號、雙引號、括號,換成英文的,不然起不了作用
卡住了……bottomsalary到新的cut_word函數之后如何賦值?
干貨滿滿啊
終于把這章一步步打了一遍
不不.人多。我編程也沒問題,學到統計學,看那些公式,有點卡住了
堅持到現在的只有3人了嗎 ??
axis=1表示將函數用在行,axis=1則是列? 到底是行還是列?
文中的練習題哪里下載呢
卡頓