Python:探究Matplotlib直方圖繪制中的參數bins和rwidth


1 情境引入

我們在做機器學習相關項目時,常常會分析數據集的樣本分布,而這就需要用到直方圖的繪制。

在Python中可以很容易地調用matplotlib.pyplothist函數來繪制直方圖。不過,該函數參數不少,有幾個繪圖的小細節也需要注意。

首先,我們假定現在有個聯邦學習的項目情景。我們有一個樣本個數為15的圖片數據集,樣本標簽有3個,分別為cat, dog, car。這個數據集已經被不均衡地划分到4個客戶端(client)上,如像下面表示:

n_clients = 4
classes = ["cat", "dog", "car"]
# 表示每種label的樣本都划分到了哪些client上
# 字典value為client ID組成的列表
label_to_cids = {"cat": [0, 0, 2, 3], "dog": [
    0, 1, 1, 2, 3, 3, 3], "car": [0, 2, 2, 3]}

如何我們需要可視化cat類別的樣本在客戶端的分布情況,我們可以寫出如下代碼:

plt.figure(figsize=(5, 3))
plt.hist(label_to_cids["cat"], stacked=False, bins=n_clients, label="cat")

plt.xticks(np.arange(n_clients), [
           "Client {}".format(i) for i in range(n_clients)])
plt.legend()
plt.show()

此時的可視化結果如下:

NLP多任務學習

在這個示例中,我們所調用的hist函數以cat類別的樣本所屬的Client ID為輸入值,並將這個由樣本的client ID所組成的列表按照[0, 1, 2, 3]這個等距分段划分到了4個bins(箱子[1])里(分別對應4個client ID),並統計每個bins中的樣本數量。

如果需要可視化所有類別的樣本在客戶端的分布情況,我們可以將代碼修改為:

plt.figure(figsize=(5, 3))
plt.hist(label_to_cids.values(), stacked=False, bins=n_clients, label=classes)

plt.xticks(np.arange(n_clients), [
           "Client {}".format(i) for i in range(n_clients)])
plt.legend()
plt.show()

此時的可視化結果如下:

NLP多任務學習

這個示例和我們上一個示例稍有不同,此時的輸入值不再是單個列表,而是一組不定長列表,其中每個列表對應一種類別的樣本分布情況。但原理和示例一相同,我們依次將每個類別的client ID列表划分到4個bins中(圖中不同顏色的bar(條)即對應不同的類別)。

這時我們會發現,我們x軸上的標簽和上方的bar並沒有對齊(在示例一中,1個bin包含1種類型的bar;在示例二中,1個bin包含3種類型的bar),而這時需要我們調整bins這個參數。

2 bins 參數

在講述bins參數之前我們先來熟悉一下hist繪圖中bin和bar的含義。下面是它們的詮釋圖:

NLP多任務學習

這里\(x_1\)\(x_2\)是x軸對象,在hist中,默認x軸第一個對象對應刻度為0,第2個對象刻度為1,依次類圖。在這個詮釋圖上,bin(原意為箱子)就是指每個x軸對象所占優的矩形繪圖區域,bar(原意為條)就是指每個矩形繪圖區域中的條形。 如上圖所示,x軸第一個對象對應的bin區間為[-0.5, 0.5),第2個對象對應的bin區域為[0.5, 1)(注意,hist規定一定是左閉又開)。每個對象的bin區域內都有3個bar。

如果讀者學過《隨機算法》[2]課程,應該就會反應過來,這其實就是球與箱子模型。我們將輸入數組視為球,將bin視為箱子,那么我們這里就是要可視化球在箱子里的分布情況,也即可視化每個箱子里球的個數。

通過查閱matplotlib文檔[3],我們知道了bins參數的解釋如下:

bins: int or sequence or str, default: rcParams["hist.bins"] (default: 10)
If bins is an integer, it defines the number of equal-width bins in the range.
If bins is a sequence, it defines the bin edges, including the left edge of the first bin and the right edge of the last bin; in this case, bins may be unequally spaced. All but the last (righthand-most) bin is half-open. In other words, if bins is:

[1, 2, 3, 4]

then the first bin is [1, 2) (including 1, but excluding 2) and the second [2, 3). The last bin, however, is [3, 4], which includes 4.
If bins is a string, it is one of the binning strategies supported by numpy.histogram_bin_edges: 'auto', 'fd', 'doane', 'scott', 'stone', 'rice', 'sturges', or 'sqrt'.

我來概括一下,也就是說如果bins是個數字,那么它設置的是bin的個數,也就是沿着x軸划分多少個獨立的繪圖區域。我們這里有四個client,故需要設置4個繪圖區域,每個區域相對於x軸刻度的偏移采取默認設置。

不過,如果我們要設置每個區域的位置偏移,我們就需要將bins設置為一個序列。

bins序列的刻度要參照hist函數中的x坐標刻度來設置,本任務中4個分類類別對應的x軸刻度分別為[0, 1, 2, 3] 。如果我們將序列設置為[0, 1, 2, 3, 4]就表示第一個繪圖區域對應的區間是[1, 2),第2個繪圖區域對應的位置是[1, 2),第三個繪圖區域對應的位置是[2, 3),依次類推。

就大眾審美而言,我們想讓每個區域的中心和對應x軸刻度對齊,這第一個區域的區間為[-0.5, 0.5),第二個區域的區間為[0.5, 1.5),依次類推。則最終的bins序列為[-0.5, 0.5, 1.5, 2.5, 3.5]。於是,我們將hist函數的bins參數修改為np.arange(-0.5, 4, 1)

plt.hist(label_to_cids.values(), stacked=False,
         bins=np.arange(-0.5, 4, 1), label=classes)

這樣,每個划分區域和對應x軸的刻度就對齊了:

NLP多任務學習

3 stacked參數

有時x軸的項目多了,每個x軸的對象都要設置3個bar對繪圖空間無疑是一個巨大的占用。在這個情況下我們如何壓縮空間的使用呢?這個時候參數stacked就派上了用場,我們將參數stacked設置為True:

plt.hist(label_to_cids.values(), stacked=True,
         bins=np.arange(-0.5, 4, 1), label=classes)

可以看到每個x軸對象的bar都“疊加”起來了:

NLP多任務學習

不過,新的問題又出來了,這樣每x軸對象的bar之間完全沒有距離了,顯得十分“擁擠”(事實上,我們在本文第1部分對cat類別的樣本在客戶端的分布情況進行可視化時,就已經遇到了這種情況)。 我們可否修改bins參數以設置區域bin之間的間距呢?答案是不行,因為我們前面提到過,bins參數中只能將區域設置為連續排布的。

換一個思路,我們設置每個bin內的bar和bin邊界之間的間距。此時,我們需要修改r_width參數。

4 rwidth 參數

我們看文檔中對rwidth參數的解釋:

rwidth: float or None, default: None
The relative width of the bars as a fraction of the bin width. If None, automatically compute the width.
Ignored if histtype is 'step' or 'stepfilled'.

翻譯一下,rwidth用於設置每個bin中的bar相對bin的大小。這里我們不妨修改為0.5:

plt.hist([train_labels[idc]for idc in client_idcs],stacked=True, 
         bins=np.arange(-0.5, 4, 1), rwidth=0.5, 
        label=["Client {}".format(i) for i in range(N_CLIENTS)])

修改之后的圖表如下:

NLP多任務學習

可以看到每個x軸元素內的bar正好占對應bin的寬度的二分之一。

參考

  • [1] McKinney W. Python for data analysis: Data wrangling with Pandas, NumPy, and IPython, 2nd edition[M]. " O'Reilly Media, Inc.", 2018.
  • [2] Mitzenmacher M, Upfal E. Probability and computing: Randomization and probabilistic techniques in algorithms and data analysis, second edition[M]. Cambridge university press, 2017.
  • [3] Matplotlib: matplotlib.pyplot.hist


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM