Python 100 Days Day75 Introduction to Matplotlib
27 Sep 2022 | beginner pythonauthor: jackfrued
數據可視化-1
在完成了對數據的透視之後,我們可以將數據透視的結果通過可視化的方式呈現出來,簡單的說,就是將數據變成漂亮的統計圖表,然後進一步發現和解讀數據背後隱藏的商業價值。在之前的課程中,我們已經為大家展示過用使用Series或DataFrame對象的plot方法生成可視化圖表的操作,本章我們為大家講解plot方法的基石,它就是大名鼎鼎的matplotlib庫。
常用的圖表類型
常用的圖表類型及其應用場景如下圖所示。

Matplotlib 的安裝和導入
如果還沒有安裝matplotlib庫,可以使用 Python 的包管理工具 pip 來安裝,命令如下所示。
pip install matplotlib
在 Notebook 中,我們可以用下面的方式導入matplotlib。為了解決圖表中文顯示的問題,我們可以通過pyplot模塊的rcParams屬性修改配置參數,具體的操作如下所示。
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei', 'Songti SC']
plt.rcParams['axes.unicode_minus'] = False
說明:上面代碼中的
SimHei是字體名稱,大家可以通過百度雲盤下載並安裝該字體,鏈接地址:https://pan.baidu.com/s/1rQujl5RQn9R7PadB2Z5g_g,提取碼:e7b4;Songti SC是我的 macOS 上自帶的字體,對於 macOS 或 Windows 系統,字體的名字都可以在用戶主目錄下的.matplotlib文件夾下的fontlist-v330.json文件中找到。值得注意的是,使用中文字體後坐標軸上的負號會顯示不出來,所以需要將axes.unicode_minus參數設置為False,這樣才能讓坐標軸上的負號正常顯示。
通過下面的魔法指令,我們可以在繪圖時生成矢量圖(SVG - Scalable Vector Graphics)。
%config InlineBackend.figure_format='svg'
繪圖的流程
創建畫布
pyplot模塊的figure函數可以用來創建畫布,創建畫布時,可以通過figsize參數指定畫布的尺寸(默認值是[6.4, 4.8]);可以通過dpi參數設置繪圖的分辨率,因為dpi代表了每英寸的像素點數量。除此之外,還可以通過facecolor參數設置畫布的背景色。figure函數的返回值是一個Figure對象,它代表了繪圖使用的畫布,我們可以基於畫布來創建繪圖使用的坐標系。
plt.figure(figsize=(8, 4), dpi=120, facecolor='darkgray')
創建坐標系
可以直接使用pyplot模塊的subplot函數來創建坐標系,該函數會返回Axes對象。subplot的前三個參數分別用來指定整個畫布分成幾行幾列以及當前坐標系的索引,這三個參數的默認值都是1。如果需要在畫布上創建多個坐標系,就需要使用該函數,否則就直接使用默認的也是唯一的坐標系。當然,也可以通過上面創建的Figure對象的add_subplot方法或add_axes方法來創建坐標系,前者跟subplot函數的作用一致,後者會產生嵌套的坐標系。
plt.subplot(2, 2, 1)
繪制圖像
折線圖
在繪圖時,如果沒有先調用figure函數和subplot函數,我們將使用默認的畫布和坐標系,如果要繪制折線圖,可以使用pyplot模塊的plot函數,並指定橫軸和縱軸的數據。折線圖最適合用來觀察數據的趨勢,尤其是當橫坐標代表時間的情況下。我們可以使用plot函數的color參數來定制折線的顏色,可以使用marker參數來定制數據點的標記(例如:*表示五角星,^表示三角形,o表示小圓圈等),可以使用linestyle參數來定制折線的樣式(例如:-表示實線,--表示虛線,:表示點線等),可以使用linewidth參數來定制折線的粗細。 下面的代碼繪制了一條正弦曲線,其中marker='*'會將數據點的標記設置為五角星形狀,而color='red'會將折線繪制為紅色。
import numpy as np
x = np.linspace(-2 * np.pi, 2 * np.pi, 120)
y = np.sin(x)
# 創建畫布
plt.figure(figsize=(8, 4), dpi=120)
# 繪制折線圖
plt.plot(x, y, linewidth=2, marker='*', color='red')
# 顯示繪圖
plt.show()

如果要在一個坐標系上同時繪制正弦和余弦曲線,可以對上面的代碼稍作修改。
x = np.linspace(-2 * np.pi, 2 * np.pi, 120)
y1, y2 = np.sin(x), np.cos(x)
plt.figure(figsize=(8, 4), dpi=120)
plt.plot(x, y1, linewidth=2, marker='*', color='red')
plt.plot(x, y2, linewidth=2, marker='^', color='blue')
# 定制圖表的標注,其中的arrowprops是定制箭頭樣式的參數
plt.annotate('sin(x)', xytext=(0.5, -0.75), xy=(0, -0.25), fontsize=12, arrowprops={
'arrowstyle': '->', 'color': 'darkgreen', 'connectionstyle': 'angle3, angleA=90, angleB=0'
})
plt.annotate('cos(x)', xytext=(-3, 0.75), xy=(-1.25, 0.5), fontsize=12, arrowprops={
'arrowstyle': '->', 'color': 'darkgreen', 'connectionstyle': 'arc3, rad=0.35'
})
plt.show()

如果要使用兩個坐標系分別繪制正弦和余弦,可以用上面提到的subplot函數來創建坐標系,然後再繪圖。
plt.figure(figsize=(8, 4), dpi=120)
# 創建坐標系(第1個圖)
plt.subplot(2, 1, 1)
plt.plot(x, y1, linewidth=2, marker='*', color='red')
# 創建坐標系(第2個圖)
plt.subplot(2, 1, 2)
plt.plot(x, y2, linewidth=2, marker='^', color='blue')
plt.show()

當然也可以像下面這麽做,大家可以運行代碼看看跟上面的圖有什麽區別。
plt.figure(figsize=(8, 4), dpi=120)
plt.subplot(1, 2, 1)
plt.plot(x, y1, linewidth=2, marker='*', color='red')
plt.subplot(1, 2, 2)
plt.plot(x, y2, linewidth=2, marker='^', color='blue')
plt.show()
然後,再試一試下面這個代碼,看看運行效果如何。
fig = plt.figure(figsize=(10, 4), dpi=120)
plt.plot(x, y1, linewidth=2, marker='*', color='red')
# 用Figure對象的add_axes方法在現有坐標系中嵌套一個新的坐標系
# 該方法的參數是一個四元組,代表了新坐標系在原坐標系中的位置
# 前兩個值是左下角的位置,後兩個值是坐標系的寬度和高度
ax = fig.add_axes((0.595, 0.6, 0.3,0.25))
ax.plot(x, y2, marker='^', color='blue')
ax = fig.add_axes((0.155, 0.2, 0.3,0.25))
ax.plot(x, y2, marker='^', color='green')
plt.show()
散點圖
散點圖可以幫助我們了解兩個變量的關系,如果需要了解三個變量的關系,可以將散點圖升級為氣泡圖。下面的代碼中,x和y兩個數組分別表示每個月的收入和每個月網購的支出,如果我們想了解x和y是否存在相關關系,就可以繪制如下所示的散點圖。
x = np.array([5550,7500,10500,15000,20000,25000,30000,40000])
y = np.array([800,1800,1250,2000,1800,2100,2500,3500])
plt.figure(figsize=(6, 4), dpi=120)
plt.scatter(x, y)
plt.show()

柱狀圖
在對比數據的差異時,柱狀圖是非常棒的選擇,我們可以使用pyplot模塊的bar函數來生成柱狀圖,也可以使用barh函數來生成水平柱狀圖。我們先為柱狀圖準備一些數據,代碼如下所示。
x = np.arange(4)
y1 = np.random.randint(20, 50, 4)
y2 = np.random.randint(10, 60, 4)
繪制柱狀圖的代碼。
plt.figure(figsize=(6, 4), dpi=120)
# 通過橫坐標的偏移,讓兩組數據對應的柱子分開
# width參數控制柱子的粗細,label參數為柱子添加標簽
plt.bar(x - 0.1, y1, width=0.2, label='銷售A組')
plt.bar(x + 0.1, y2, width=0.2, label='銷售B組')
# 定制橫軸的刻度
plt.xticks(x, labels=['Q1', 'Q2', 'Q3', 'Q4'])
# 定制顯示圖例
plt.legend()
plt.show()

如果想繪制堆疊柱狀圖,可以對上面的代碼稍作修改,如下所示。
labels = ['Q1', 'Q2', 'Q3', 'Q4']
plt.figure(figsize=(6, 4), dpi=120)
plt.bar(labels, y1, width=0.4, label='銷售A組')
# 注意:堆疊柱狀圖的關鍵是將之前的柱子作為新柱子的底部
# 可以通過bottom參數指定底部數據,新柱子繪制在底部數據之上
plt.bar(labels, y2, width=0.4, bottom=y1, label='銷售B組')
plt.legend(loc='lower right')
plt.show()

餅狀圖
餅狀圖通常簡稱為餅圖,是一個將數據劃分為幾個扇形區域的統計圖表,它主要用於描述數量、頻率等之間的相對關系。在餅圖中,每個扇形區域的大小就是其所表示的數量的比例,這些扇形區域合在一起剛好是一個完整的餅。在需要展示數據構成的場景下,餅狀圖、樹狀圖和瀑布圖是不錯的選擇,我們可以使用pyplot模塊的pie函數來繪制餅圖,代碼如下所示。
data = np.random.randint(100, 500, 7)
labels = ['蘋果', '香蕉', '桃子', '荔枝', '石榴', '山竹', '榴蓮']
plt.figure(figsize=(5, 5), dpi=120)
plt.pie(
data,
# 自動顯示百分比
autopct='%.1f%%',
# 餅圖的半徑
radius=1,
# 百分比到圓心的距離
pctdistance=0.8,
# 顏色(隨機生成)
colors=np.random.rand(7, 3),
# 分離距離
# explode=[0.05, 0, 0.1, 0, 0, 0, 0],
# 陰影效果
# shadow=True,
# 字體屬性
textprops=dict(fontsize=8, color='black'),
# 楔子屬性(生成環狀餅圖的關鍵)
wedgeprops=dict(linewidth=1, width=0.35),
# 標簽
labels=labels
)
# 定制圖表的標題
plt.title('水果銷售額占比')
plt.show()

說明:大家可以試一試將上面代碼中被注釋的部分恢覆,看看有什麽樣的效果。
直方圖
在統計學中,直方圖是一種展示數據分布情況的圖形,是一種二維統計圖表,它的兩個坐標分別是統計樣本和該樣本對應的某個屬性的度量。下面的數據是某學校100名男學生的身高,如果我們想知道數據的分布,就可以使用直方圖。
heights = np.array([
170, 163, 174, 164, 159, 168, 165, 171, 171, 167,
165, 161, 175, 170, 174, 170, 174, 170, 173, 173,
167, 169, 173, 153, 165, 169, 158, 166, 164, 173,
162, 171, 173, 171, 165, 152, 163, 170, 171, 163,
165, 166, 155, 155, 171, 161, 167, 172, 164, 155,
168, 171, 173, 169, 165, 162, 168, 177, 174, 178,
161, 180, 155, 155, 166, 175, 159, 169, 165, 174,
175, 160, 152, 168, 164, 175, 168, 183, 166, 166,
182, 174, 167, 168, 176, 170, 169, 173, 177, 168,
172, 159, 173, 185, 161, 170, 170, 184, 171, 172
])
可以使用pyplot模塊的hist函數來繪制直方圖,代碼如下所示。
# 將身高數據分到以下8個組中
bins = np.array([150, 155, 160, 165, 170, 175, 180, 185, 190])
plt.figure(figsize=(6, 4), dpi=120)
# density參數默認值為False,表示縱軸顯示頻數
# 將density參數設置為True,縱軸會顯示概率密度
plt.hist(heights, bins, density=True)
# 定制橫軸標簽
plt.xlabel('身高')
# 定制縱軸標簽
plt.ylabel('概率密度')
plt.show()

箱線圖
箱線圖又叫箱型圖或盒須圖,是一種用於展示一組數據分散情況的統計圖表,如下所示。因圖形如箱子,而且在上下四分位數之外有線條像胡須延伸出去而得名。在箱線圖中,箱子的上邊界是上四分位數($Q_3$)的位置,箱子的下邊界是下四分位數($Q_1$)的位置,箱子中間的線條是中位數($Q_2$)的位置,而箱子的長度就是四分位距離(IQR)。除此之外,箱子上方線條的邊界是最大值,箱子下方線條的邊界是最小值,這兩條線之外的點就是離群值(outlier)。所謂離群值,是指數據小於$Q_1 - 1.5 \times IQR$或數據大於$Q_3 + 1.5 \times IQR$的值,公式中的1.5還可以替換為3來發現極端離群值(extreme outlier),而介於1.5到3之間的離群值通常稱之為適度離群值(mild outlier)。
可以使用pyplot模塊的boxplot函數來繪制箱線圖,代碼如下所示。
# 數組中有47個[0, 100)範圍的隨機數
data = np.random.randint(0, 100, 47)
# 向數組中添加三個可能是離群點的數據
data = np.append(data, 160)
data = np.append(data, 200)
data = np.append(data, -50)
plt.figure(figsize=(6, 4), dpi=120)
# whis參數的默認值是1.5,將其設置為3可以檢測極端離群值
# showmeans=True表示在圖中標記均值的位置
plt.boxplot(data, whis=1.5, showmeans=True, notch=True)
# 定制縱軸的取值範圍
plt.ylim([-100, 250])
# 定制橫軸的刻度
plt.xticks([1], labels=['data'])
plt.show()

說明:由於數據是隨機生成的,所以大家運行上面的代碼生成的圖可能跟我這里並不相同。
顯示或保存圖像
可以使用pyplot模塊的show函數來顯示繪制的圖表,我們在上面的代碼中使用過這個函數。如果希望保存圖表,可以使用savefig函數。需要注意的是,如果要同時顯示和保存圖表,應該先執行savefig函數,再執行show函數,因為在調用show函數時,圖表已經被釋放,位於show函數之後的savefig保存的只是一個空白的區域。
plt.savefig('chart.png')
plt.show()
其他圖表
使用 matplotlib,我們還可以繪制出其他的統計圖表(如:雷達圖、玫瑰圖、熱力圖等),但實際工作中,使用頻率最高的幾類圖表我們在上面已經為大家完整的展示出來了。此外,matplotlib 還有很多對統計圖表進行定制的細節,例如定制坐標軸、定制圖表上的文字和標簽等。如果想了解如何用 matplotlib 繪制和定制更多的統計圖表,可以直接查看 matplotlib 官方網站上的文檔和示例。
Comments