需求
客户希望绘制一个特殊的饼状图,具体需求如下:
- 每个扇形外侧都有一个指向扇形的注释,要有箭头;
- 能够指定每个扇形的半径;
- 能够指定每个扇形的颜色;
- 饼状图要有阴影;
- 各个扇形要有一定的凸起。
实现
首先,针对需求我们进行一下分析,先从需求 2 看起,plt.pie()
方法提供了一个关键字参数 radius
,通过这个参数可以指定饼状图的半径,但是这里只能传递一个浮点数,不能传递一个数组,也就是说不能单独指定每一个扇形的半径,如果传递一个容器类型,则会抛出 TypeError: 'radius' must be an instance of numbers.Number, not a list
错误。因此我们只得另辟蹊径,首先 plt.pie()
方法是有返回值的,一般情况下他会返回两个值,第一个是包含所有扇形 Artist 子实例的 Wedge 对象列表;第二个是包含所有扇形标签的 Text 对象列表;除此之外,如果 autopct
参数不为空,则会返回第三个值,从需求 1 和需求 2 来看,我们就需要前两个返回值即可,因此代码是 wedges, texts, *_ = plt.pie(...)
。通过 Wedge 对象的 set_radius()
方法完成需求 2。这里提一嘴,由于我们设置了阴影,调用 Axes.patches
属性不光会获取饼状图所有的 Wedge 对象,也会获取所有的阴影 Shadow对象,当然我们可以进行过滤,但是实现起来多了一步,没那个必要。
需求 3、需求 4 和需求 5 非常简单,plt.pie()
方法本身提供了相关的关键字参数,只需要指定 shadow=True
、 color=colors[i]
和 explode=explodes
就可以实现。真正的难点是需求 1,所以重点说一下,我们都知道 plt.pie()
方法提供了 labels
和 labeldistance
这两个参数,这两个参数可以控制每个标签和标签的位置,但是这样无法实现箭头功能,因此使用 plt.pie()
方法本身提供的能力是无法实现需求的。从需求 1 上来看,绘制要有注释和箭头,这两个名词放在一起我们应该非常敏感才对,这不就是典型的指向型注释嘛,因此我们需要使用 annotate
方法实现需求 1,知道了方法和目标,实现起来就很简单了,至此所有的需求都已经被实现了。
最后,整个程序的完整代码如下:
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)) # 设置绘图区域大小
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()
画图结果如下:
往期回顾
文中难免会出现一些描述不当之处(尽管我已反复检查多次),欢迎在留言区指正,相关的知识点也可进行分享,希望大家都能有所收获!!如果觉得我的文章写得还行,不妨支持一下。你的每一个转发、关注、点赞、评论都是对我最大的支持!