如何在matplotlib中加注釋和內嵌圖
在用Matplotlib進行可視化過程中,很多時候為了更直觀地展現數據大小,會將具體的數值標注在圖形中,比如在柱狀圖上標明數值大小。
這篇文章會以一個實際的案例,詳細講講如何給數據加注解,同時也介紹一下一種比較騷的操作,即Matplotlib的內嵌圖(把一張小圖嵌入到一張大圖中),學會這個后,你能夠繪制出這種圖形
數據注釋
演示的數據集為2016年抵美(到達美國)人數排名前十的國家數據,包含國名和具體入境人數,人數的具體單位為百萬人次
- import pandas as pd
- import numpy as np
- import matplotlib.pyplot as plt
- %matplotlib inline
- plt.rcParams['font.sans-serif'] = 'SimHei'
- x_data = ['加拿大','墨西哥','英國','日本','中國','德國','韓國','法國','巴西','印度']
- y_data = [13.428347,12.255948,3.019111,2.352919,2.09833,1.322882,1.282097,1.127868,1.109066,0.874016]
有了數據后,可以非常快地畫出一張柱狀圖
- fig,ax = plt.subplots(figsize=(16,6))
- ax.bar(x_data,y_data)
接下來開始加注釋,即在柱狀圖上顯示具體數值。在Matplotlib中,為數據加上注釋有兩種方式,一種是使用ax.text(),另一種則是ax.annotate()。
ax.text()
ax.text()的主要作用是為圖中加上一些text,也就是文字。不僅是能夠加注釋,只要指定了坐標,可以在圖上的任何坐標加上text。
函數中的幾個重要的參數,具體介紹一下
- x:x的坐標
- y:y的坐標
- s:要加的文字
- rotation:文字旋轉的角度
- fontsize:文字字體大小
- fontweight:文字字體粗細
需要注意的是,每次調用ax.text()只能生成一個Text對象,也就是說每次只能加一個注釋,多個的話需要寫循環生成。
所以一般加注釋是這么一個流程:先確定注釋的橫縱坐標-->寫循環調用ax.text()
- fig,ax = plt.subplots(figsize=(16,6))
- ax.bar(x_data,y_data)
- # 循環生成text
- # horizontalalignment參數設置注釋居中顯示
- for x,y in zip(x_data,y_data):
- ax.text(x,y+0.05,y,fontsize=14,horizontalalignment='center')
關于圖表美化方面,這里暫時不過多贅述。
ax.annotate()
相比于ax.text(),ax.annotate()更像是專門為做注釋而生的,annotate便是注釋的意思。
ax.annotate()的注釋功能更強大,除了加入文本注釋外,如果有需要還能夠加上箭頭→進行指示。
一些主要的參數如下:
- s:注釋文本
- xy:要加注釋的數據點位置
- xytext:文本注釋的位置,默認情況下為xy
- arrowprops:一個控制箭頭的屬性的dict,如果需要顯示箭頭,必須要設置
這里比較有意思的兩個參數是xy和xytext,二者貌似沒啥區別的樣子,這個地方確實很容易產生困惑。
一般情況下,s和xy是必須要設置的參數,如果不指定,xytext默認和xy一致。
但如果要設置箭頭的話,xy的坐標則定義了箭頭的頭部,xytext則指定箭頭的尾部和文本注釋的位置,實際畫圖來理解看看。
ax.annotate()和ax.text()的畫圖流程是一致的,都需要循環生成注釋。
不設置箭頭,簡單加上注釋
- fig,ax = plt.subplots(figsize=(16,6))
- ax.bar(x_data,y_data)
- for x,y in zip(x_data,y_data):
- ax.annotate(y,(x,y+0.05),fontsize=14,horizontalalignment='center')
加上注釋并設置箭頭
- fig,ax = plt.subplots(figsize=(16,6))
- ax.bar(x_data,y_data)
- # 箭頭頭部坐標(x,y)
- # 箭頭尾部坐標(注釋坐標)為(x,y+1)
- for x,y in zip(x_data,y_data):
- ax.annotate(y,xy = (x,y),xytext = (x,y+1),fontsize=14,horizontalalignment='center',
- arrowprops =dict(arrowstyle='->'))
- # 將縱坐標范圍擴大
- ax.set_ylim([0,16])
設置了顯示箭頭之后,可以明顯地看出,箭頭是由xytext坐標指向xy的坐標的,所以,當你不需要設置箭頭的時候,xytext設置的意義并不大。
內嵌圖
內嵌圖大家可能用的比較少,但這種圖其實還是挺有用的。
拿上面的數據例子來說,柱狀圖顯示了2016年抵美人數排名前十的國家的具體人數,如果這時想結合各大地域抵美人數的占比數據進行分析,該如何繪圖?
一個比較直接的想法是用subplots子圖來實現,比如上邊顯示柱狀圖,下邊顯示餅圖,如下:
- zhou_name = ['西歐','亞洲','南美洲','大洋洲','加勒比地區','中東地區','中美洲','東歐','非洲']
- zhou_percent = [36.2,30.8,13.9,4.3,4.1,3.8,2.8,2.6,1.5]
- fig,ax = plt.subplots(2,1,figsize=(16,12))
- ax[0].bar(x_data,y_data)
- for x,y in zip(x_data,y_data):
- ax[0].annotate(y,(x,y+0.05),fontsize=14,horizontalalignment='center')
- ax[1].pie(zhou_percent,labels=zhou_name,autopct='%1.1f%%')
這當然是可行的,但還有更好的方案,那便是內嵌圖。注意到這里的柱狀圖右側有很大的留白部分,如果把餅圖放到柱狀圖右側的留白部分會顯得更加直觀。
內嵌圖有兩種生成方式,一種是fig.add_axes(),另一種則是使用inset_axes()。
fig.add_axes()
fig.add_axes()就是在原有的Figure上加上一個新的區域Axes,然后在這個區域中繪制圖形。
使用這個方法的話需要指定新增的這個區域Axes在Figure中的相對位置和區域大小,輸入參數均為相對于原來Figure的比例值,如下:
- # left和bottom控制新Axes的位置
- # width和height控制新Axes的大小(長寬)
- # 這些均用相對數來表示,大小在0-1之間
- left,bottom,width,height = [0.5,0.3,0.5,0.5]
- fig,ax1 = plt.subplots(figsize=(16,6))
- ax1.bar(x_data,y_data)
- for x,y in zip(x_data,y_data):
- ax1.annotate(y,(x,y+0.05),fontsize=14,horizontalalignment='center')
- # 運用fig.add_axes()新增一個區域Axes繪圖
- ax2 = fig.add_axes([left,bottom,width,height])
- ax2.pie(zhou_percent,labels=zhou_name,autopct='%1.1f%%')
inset_axes
相比于fig.add_axes()需要對相對位置進行調試,使用inset_axes()進行繪圖則可以方便進行定位。
inset_axes中的位置由參數loc設置,可以用字符串控制,也可以輸入數字,具體如下:
- 'upper right' : 1
- 'upper left' : 2
- 'lower left' : 3
- ......
- 'upper center' : 9
- 'center' : 10
- # 使用前需要先導包
- from mpl_toolkits.axes_grid1.inset_locator import inset_axes
- fig,ax1 = plt.subplots(figsize=(16,6))
- ax1.bar(x_data,y_data)
- for x,y in zip(x_data,y_data):
- ax1.annotate(y,(x,y+0.05),fontsize=14,horizontalalignment='center')
- # 將內嵌圖置于右側,寬度和長度分別為相對長度
- ax2 = inset_axes(ax1,width = '60%',height = '60%',loc='right')
- ax2.pie(zhou_percent,labels=zhou_name,autopct='%1.1f%%')
美化方面這里就不再多講了,具體可以參照這篇文章如何用 Matplotlib 畫一張好看的圖。