需求
客戶希望繪制一個(gè)特殊的餅狀圖,具體需求如下:
- 每個(gè)扇形外側(cè)都有一個(gè)指向扇形的注釋,要有箭頭;
- 能夠指定每個(gè)扇形的半徑;
- 能夠指定每個(gè)扇形的顏色;
- 餅狀圖要有陰影;
- 各個(gè)扇形要有一定的凸起。
實(shí)現(xiàn)
首先,針對(duì)需求我們進(jìn)行一下分析,先從需求 2 看起,plt.pie() 方法提供了一個(gè)關(guān)鍵字參數(shù) radius,通過(guò)這個(gè)參數(shù)可以指定餅狀圖的半徑,但是這里只能傳遞一個(gè)浮點(diǎn)數(shù),不能傳遞一個(gè)數(shù)組,也就是說(shuō)不能單獨(dú)指定每一個(gè)扇形的半徑,如果傳遞一個(gè)容器類型,則會(huì)拋出 TypeError: 'radius' must be an instance of numbers.Number, not a list 錯(cuò)誤。因此我們只得另辟蹊徑,首先 plt.pie() 方法是有返回值的,一般情況下他會(huì)返回兩個(gè)值,第一個(gè)是包含所有扇形 Artist 子實(shí)例的 Wedge 對(duì)象列表;第二個(gè)是包含所有扇形標(biāo)簽的 Text 對(duì)象列表;除此之外,如果 autopct 參數(shù)不為空,則會(huì)返回第三個(gè)值,從需求 1 和需求 2 來(lái)看,我們就需要前兩個(gè)返回值即可,因此代碼是 wedges, texts, *_ = plt.pie(...)。通過(guò) Wedge 對(duì)象的 set_radius() 方法完成需求 2。這里提一嘴,由于我們?cè)O(shè)置了陰影,調(diào)用 Axes.patches 屬性不光會(huì)獲取餅狀圖所有的 Wedge 對(duì)象,也會(huì)獲取所有的陰影 Shadow對(duì)象,當(dāng)然我們可以進(jìn)行過(guò)濾,但是實(shí)現(xiàn)起來(lái)多了一步,沒(méi)那個(gè)必要。
需求 3、需求 4 和需求 5 非常簡(jiǎn)單,plt.pie() 方法本身提供了相關(guān)的關(guān)鍵字參數(shù),只需要指定 shadow=True 、 color=colors[i] 和 explode=explodes 就可以實(shí)現(xiàn)。真正的難點(diǎn)是需求 1,所以重點(diǎn)說(shuō)一下,我們都知道 plt.pie() 方法提供了 labels 和 labeldistance 這兩個(gè)參數(shù),這兩個(gè)參數(shù)可以控制每個(gè)標(biāo)簽和標(biāo)簽的位置,但是這樣無(wú)法實(shí)現(xiàn)箭頭功能,因此使用 plt.pie() 方法本身提供的能力是無(wú)法實(shí)現(xiàn)需求的。從需求 1 上來(lái)看,繪制要有注釋和箭頭,這兩個(gè)名詞放在一起我們應(yīng)該非常敏感才對(duì),這不就是典型的指向型注釋嘛,因此我們需要使用 annotate 方法實(shí)現(xiàn)需求 1,知道了方法和目標(biāo),實(shí)現(xiàn)起來(lái)就很簡(jiǎn)單了,至此所有的需求都已經(jīng)被實(shí)現(xiàn)了。
最后,整個(gè)程序的完整代碼如下:
import matplotlib.pyplot as plt
import numpy as np
data = '0.37 0.02 0.24 0.26 0.11'.split()
data = [float(i) for i in data]
explode = [0.01, 0.02, 0.01, 0.01, 0.02]
colors = ['#ACCBD0', '#E56F32', '#58C5C0', '#B9CC58', '#9C8679']
radiuses = [1.33, 1, 1.2, 1.3, 1.05]
fig, ax = plt.subplots(figsize=(6, 6)) # 設(shè)置繪圖區(qū)域大小
wedges, texts = ax.pie(data, startangle=0, explode=explode, shadow=True, colors=colors, radius=radiuses)
kw = dict(arrowprops=dict(arrowstyle='-'),
zorder=0, va='center')
for i, p in enumerate(wedges):
ang = (p.theta2 - p.theta1) / 2. + p.theta1
y = np.sin(np.deg2rad(ang))
x = np.cos(np.deg2rad(ang))
horizontalalignment = {-1: 'right', 1: 'left'}[int(np.sign(x))]
connectionstyle = f'angle,angleA=0,angleB={ang}'
kw['arrowprops'].update({'connectionstyle': connectionstyle})
ax.annotate(data[i], xy=(x, y), xytext=(1.35 * np.sign(x), 1.4 * y), color=colors[i],
horizontalalignment=horizontalalignment, **kw)
ax.axis('equal')
for text, wedge, radius in zip(texts, wedges, radiuses):
wedge.set_radius(radius)
plt.show()
畫圖結(jié)果如下:

往期回顧
文中難免會(huì)出現(xiàn)一些描述不當(dāng)之處(盡管我已反復(fù)檢查多次),歡迎在留言區(qū)指正,相關(guān)的知識(shí)點(diǎn)也可進(jìn)行分享,希望大家都能有所收獲??!如果覺(jué)得我的文章寫得還行,不妨支持一下。你的每一個(gè)轉(zhuǎn)發(fā)、關(guān)注、點(diǎn)贊、評(píng)論都是對(duì)我最大的支持!