在完成了對資料的透視之後,我們可以將資料透視的結果透過視覺化的方式呈現出來,簡單的說,就是將資料變成漂亮的統計圖表,然後進一步發現和解讀資料背後隱藏的商業價值。在之前的課程中,我們已經為大家展示過用使用Series
或DataFrame
物件的plot
方法生成視覺化圖表的操作,本章我們為大家講解plot
方法的基石,它就是大名鼎鼎的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 官方網站上的文件和示例。