5種方法教你用Python玩轉histogram直方圖
關鍵時刻,第一時間送達!
閱讀本文需要 10 分鐘
直方圖是一個可以快速展示數據概率分布的工具,直觀易於理解,並深受數據愛好者的喜愛。大家平時可能見到最多就是matplotlib,seaborn等高級封裝的庫包,類似以下這樣的繪圖。
本篇博主將要總結一下使用Python繪製直方圖的所有方法,大致可分為三大類(詳細劃分是五類,參照文末總結):
純Python實現直方圖,不使用任何第三方庫
使用Numpy來創建直方圖總結數據
使用matplotlib,pandas,seaborn繪製直方圖
下面,我們來逐一介紹每種方法的來龍去脈。
純Python實現histogram
當準備用純Python來繪製直方圖的時候,最簡單的想法就是將每個值出現的次數以報告形式展示。這種情況下,使用字典來完成這個任務是非常合適的,我們看看下面代碼是如何實現的。
我們看到,count_elements()返回了一個字典,字典里出現的鍵為目標列表裡面的所有唯一數值,而值為所有數值出現的頻率次數。hist[i] = hist.get(i, 0) + 1實現了每個數值次數的累積,每次加一。
實際上,這個功能可以用一個Python的標準庫collection.Counter類來完成,它兼容Pyhont 字典並覆蓋了字典的.update()方法。
可以看到這個方法和前面我們自己實現的方法結果是一樣的,我們也可以通過collection.Counter來檢驗兩種方法得到的結果是否相等。
我們利用上面的函數重新再造一個輪子ASCII_histogram,並最終通過Python的輸出格式format來實現直方圖的展示,代碼如下:
這個函數按照數值大小順序進行繪圖,數值出現次數用(+)符號表示。在字典上調用sorted()將會返回一個按鍵順序排列的列表,然後就可以獲取相應的次數counted[k]。
這個代碼中,vals內的數值是不重複的,並且每個數值出現的頻數是由我們自己定義的,在5和15之間隨機選擇。然後運用我們上面封裝的函數,就得到了純Python版本的直方圖展示。
總結:純python實現頻數表(非標準直方圖),可直接使用collection.Counter方法實現。
使用Numpy實現histogram
以上是使用純Python來完成的簡單直方圖,但是從數學意義上來看,直方圖是分箱到頻數的一種映射,它可以用來估計變數的概率密度函數的。而上面純Python實現版本只是單純的頻數統計,不是真正意義上的直方圖。
因此,我們從上面實現的簡單直方圖繼續往下進行升級。一個真正的直方圖首先應該是將變數分區域(箱)的,也就是分成不同的區間範圍,然後對每個區間內的觀測值數量進行計數。恰巧,Numpy的直方圖方法就可以做到這點,不僅僅如此,它也是後面將要提到的matplotlib和pandas使用的基礎。
舉個例子,來看一組從拉普拉斯分布上提取出來的浮點型樣本數據。這個分布比標準正態分布擁有更寬的尾部,並有兩個描述參數(location和scale):
由於這是一個連續型的分布,對於每個單獨的浮點值(即所有的無數個小數位置)並不能做很好的標籤(因為點實在太多了)。但是,你可以將數據做分箱處理,然後統計每個箱內觀察值的數量,這就是真正的直方圖所要做的工作。
下面我們看看是如何用Numpy來實現直方圖頻數統計的。
這個結果可能不是很直觀。來說一下,np.histogram()默認地使用10個相同大小的區間(箱),然後返回一個元組(頻數,分箱的邊界),如上所示。要注意的是:這個邊界的數量是要比分箱數多一個的,可以簡單通過下面代碼證實。
那問題來了,Numpy到底是如何進行分箱的呢?只是通過簡單的np.histogram()就可以完成了,但具體是如何實現的我們仍然全然不知。下面讓我們來將np.histogram()的內部進行解剖,看看到底是如何實現的(以最前面提到的a列表為例)。
解釋一下:首先獲取a列表的最小值和最大值,然後設置默認的分箱數量,最後使用Numpy的linspace方法進行數據段分割。分箱區間的結果也正好與實際吻合,0到23均等分為10份,23/10,那麼每份寬度為2.3。
除了np.histogram之外,還存在其它兩種可以達到同樣功能的方法:np.bincount()和np.searchsorted(),下面看看代碼以及比較結果。
總結:通過Numpy實現直方圖,可直接使用np.histogram()或者np.bincount()。
使用Matplotlib和Pandas可視化Histogram
從上面的學習,我們看到了如何使用Python的基礎工具搭建一個直方圖,下面我們來看看如何使用更為強大的Python庫包來完成直方圖。Matplotlib基於Numpy的histogram進行了多樣化的封裝並提供了更加完善的可視化功能。
之前我們的做法是,在x軸上定義了分箱邊界,y軸是相對應的頻數,不難發現我們都是手動定義了分箱的數目。但是在以上的高級方法中,我們可以通過設置bins="auto"自動在寫好的兩個演算法中擇優選擇並最終算出最適合的分箱數。這裡,演算法的目的就是選擇出一個合適的區間(箱)寬度,並生成一個最能代表數據的直方圖來。
pandas.DataFrame.histogram()的用法與Series是一樣的,但生成的是對DataFrame數據中的每一列的直方圖。
繪製核密度估計(KDE)
KDE(Kernel density estimation)是核密度估計的意思,它用來估計隨機變數的概率密度函數,可以將數據變得更平緩。
使用Pandas庫的話,你可以使用plot.kde()創建一個核密度的繪圖,plot.kde() 對於 Series和DataFrame數據結構都適用。但是首先,我們先生成兩個不同的數據樣本作為比較(兩個正太分布的樣本):
以上看到,我們生成了兩組正態分布樣本,並且通過一些描述性統計參數對兩組數據進行了簡單的對比。現在,我們可以在同一個Matplotlib軸上繪製每個直方圖以及對應的kde,使用pandas的plot.kde()的好處就是:它會自動的將所有列的直方圖和kde都顯示出來,用起來非常方便,具體代碼如下:
使用Seaborn的完美替代
一個更高級可視化工具就是Seaborn,它是在matplotlib的基礎上進一步封裝的強大工具。對於直方圖而言,Seaborn有 distplot() 方法,可以將單變數分布的直方圖和kde同時繪製出來,而且使用及其方便,下面是實現代碼(以上面生成的d為例):
distplot方法默認的會繪製kde,並且該方法提供了fit參數,可以根據數據的實際情況自行選擇一個特殊的分布來對應。
注意這兩個圖微小的區別。第一種情況你是在估計一個未知的概率密度函數(PDF),而第二種情況是你是知道分布的,並想知道哪些參數可以更好的描述數據。
總結:通過seaborn實現直方圖,可使用seaborn.distplot(),seaborn也有單獨的kde繪圖seaborn.kde()。
在Pandas中的其它工具
除了繪圖工具外,pandas也提供了一個方便的.value_counts()方法,用來計算一個非空值的直方圖,並將之轉變成一個pandas的series結構,示例如下:
此外,pandas.cut()也同樣是一個方便的方法,用來將數據進行強制的分箱。比如說,我們有一些人的年齡數據,並想把這些數據按年齡段進行分類,示例如下:
除了使用方便外,更加好的是這些操作最後都會使用Cython代碼來完成,在運行速度的效果上也是非常快的。
總結:其它實現直方圖的方法,可使用.value_counts()和pandas.cut()。
該使用哪個方法?
至此,我們了解了很多種方法來實現一個直方圖。但是它們各自有什麼有缺點呢?該如何對它們進行選擇呢?當然,一個方法解決所有問題是不存在的,我們也需要根據實際情況而考慮如何選擇,下面是對一些情況下使用方法的一個推薦,僅供參考。
參考:https://realpython.com/python-histograms/
以上就是本篇所有內容,直方圖的各種玩法你get到了嗎?
※Python3標準庫簡介
※十五分鐘了解 Python 並發編程
TAG:Python |