如果想绘制指定大小的饼图,如直径5mm,可以参考本博文实现。
有此需求的起因是我有两个维度的数据想要用图形展示,第一个维度是每种场景下2021,2022和2023年的总容量,第二个维度是每种场景下2021,2022和2023年的容量占总容量的百分比。我最终选择的呈现方式是使用多个饼图进行展示这些数据。每种场景绘制一个饼图,三种渐近的颜色分别表示饼图的占比,2021-2023年的总容量则用饼图的大小来展示。为了实现上述需求,我需要画出指定大小的饼图,只有当我知道多大的饼图代表的容量是多少,我才能方便的制作图例。
当我直接使用python
的matplotlib
绘制饼图,我发现尽管我指定了radius
,我仍然不能得到我想要的尺寸的饼图,经过文档的学习我发现原因如下:
radius
指的是相对大小,当对于axes
,默认值为1,如果你设置了ax.axis('equal')
或ax.pie(..., frame=True)
等,你将没有办法改变饼的大小,如果你不设置,其含义是饼状图的实际大小与axes
的宽度和高度最小值的0.8倍之间的比值,也即,如果你设置radius=1
,你得到的饼状图的直径就与矩形axes
的宽的比值是0.8,这个是从源码得知的。
self.set(frame_on=False, xticks=[], yticks=[],
xlim=(-1.25 + center[0], 1.25 + center[0]),
ylim=(-1.25 + center[1], 1.25 + center[1]))
所以,要实现开头提到的需求,我们需要做两件事,精确控制axes
的尺寸和指定所需的radius
。举例说明如何实现这两点。我们可以开始做如下设置来创建一个figure
和两个axes
对象。其含义为创建一个宽6英寸和高4英寸的figure
对象,并在figure
对象上添加两个axes
对象,axes
创建的参数为rect=(left,bottom,width,height)
,其含义做如下解释axes_legend
左边缘与figure
左边缘的距离是figure
宽的0倍,axes_legend
下边缘与figure
下边缘的距离是figure
高的0倍,即axes_legend
右和下边缘分别与figure
右和下边缘对齐,axes_legend
的宽是figure
宽的1/3倍,高与figure
相等。axes_pie
同理。
import matplotlib.pyplot as plt
FIGURE_HEIGHT = 4
FIGURE_WIDTH = 6
fig = plt.figure(figsize=(FIGURE_WIDTH, FIGURE_HEIGHT))
axes_legend = fig.add_axes(rect=(0, 0, 1/3, 1))
axes_pie = fig.add_axes(rect=(1/3, 0, 1, 1))
plt.savefig("fig1.png", dpi=300)
这时,我们可以在`figure`的左侧4x2英寸的位置绘制图例,右侧4x4英寸的位置绘制指定大小的饼图。
PIE_SIZE = 3 # inches
labels = [f'Test{i}' for i in range(15)]
sizes = range(1, 16)
# Get the minimum value of width and height of the axes.
box = axes_pie.get_position(original=True)
AXES_RADIUS = min(box.width * FIGURE_WIDTH, box.height * FIGURE_HEIGHT)
axes_pie.pie(
sizes, labels=labels, autopct='%.2f', startangle=90,
radius= PIE_SIZE / (AXES_RADIUS*0.8)
)
handles, labels = axes_pie.get_legend_handles_labels()
axes_legend.legend(handles, labels, loc='center')
axes_legend.axis('off') # 关闭axes_legend的坐标轴
axes_pie.axhline(0, color='gray', linestyle='--', linewidth=0.5)
axes_pie.axvline(0, color='gray', linestyle='--', linewidth=0.5)
plt.savefig('fig2.png', dpi=300)
根据上述代码可以修改PIE_SIZE
来修改想要绘制的尺寸。我在stack overflow回答了一个类似的问题,在此用中文记录一下。