如何在Matplotlib饼图中仅绘制特定扇区间的分隔线
饼图仅绘制特定扇区间分隔线的实现方案
需求说明
我需要在饼图中仅绘制特定扇区间的分隔线:
- 当前图表:所有扇区间均有黑色分隔线,包括橙红色的
allocated扇区与三文鱼色的padded扇区间 - 目标图表:仅在不同类型的扇区间显示黑色分隔线,取消
allocated与padded扇区间的分隔线
已尝试方法及问题
- 通过
wedgeprops设置扇区边缘:
wedges = plt.pie(sizes, labels=labels, colors=colours, autopct='%1.1f%%') for wedge in wedges[0]: wedge.set_lw(0.5) wedge.set_edgecolor("black")
问题:该设置会作用于扇区的整个边缘,无法单独控制某一条边的显示。
- 使用
Circle绘制轮廓圆:
circle = matplotlib.patches.Circle(center, radius, fill=False, edgecolor="k", linewidth=2) ax.add_patch(circle)
问题:只能绘制完整外轮廓,无法绘制特定扇区间的分隔线;已知matplotlib.lines.Line2D但不清楚如何在饼图场景中应用。
我的完整代码
def graph_memory_layout(memory_layout): # Format: F(ree)/A(llocated)[number of bytes]P(adding)[number of bytes] [space] [next entry] # Extract infomation layout = memory_layout.split() colours = [] sizes=[] for memory in layout: first_char = memory[0] if (first_char == 'A'): allocated_bytes = 0 padded_bytes = 0 padded_flag = None for i, char in enumerate(memory[1:]): if char == 'P': padded_flag = i + 1 allocated_bytes = int(memory[1:padded_flag]) padded_bytes = int(memory[padded_flag + 1:]) payload_bytes = allocated_bytes - padded_bytes sizes.append(payload_bytes) colours.append(ALLOCATED_COLOUR) if padded_bytes is not 0: sizes.append(padded_bytes) colours.append(PADDED_COLOUR) # 这里可以记录哪些相邻扇区不需要分隔线,后续使用 elif (first_char == 'F'): sizes.append(memory[1:]) colours.append(FREE_COLOUR) # Create pie chart wedges = plt.pie(sizes, colors=colours) for wedge in wedges[0]: wedge.set_lw(0.5) wedge.set_edgecolor("black") plt.axis("equal") plt.title("Memory Layout") # Draw the chart plt.show()
解决方案
核心思路
- 数据解析阶段:记录不需要分隔线的相邻扇区索引对(即
allocated紧跟padded的情况) - 饼图绘制阶段:先移除所有扇区的默认边缘线
- 按需绘制分隔线:遍历所有扇区分隔线角度,仅保留需要显示的分隔线,跳过标记的无分隔线位置
修改后的完整代码
import matplotlib.pyplot as plt import numpy as np # 定义颜色常量 ALLOCATED_COLOUR = 'orangered' PADDED_COLOUR = 'salmon' FREE_COLOUR = 'lightblue' def graph_memory_layout(memory_layout): # Format: F(ree)/A(llocated)[number of bytes]P(adding)[number of bytes] [space] [next entry] # Extract infomation layout = memory_layout.split() colours = [] sizes = [] # 记录不需要分隔线的相邻扇区索引对 no_divider_pairs = [] current_idx = 0 for memory in layout: first_char = memory[0] if first_char == 'A': padded_flag = memory.index('P') allocated_bytes = int(memory[1:padded_flag]) padded_bytes = int(memory[padded_flag + 1:]) payload_bytes = allocated_bytes - padded_bytes sizes.append(payload_bytes) colours.append(ALLOCATED_COLOUR) if padded_bytes != 0: sizes.append(padded_bytes) colours.append(PADDED_COLOUR) # 标记当前allocated扇区与下一个padded扇区间不需要分隔线 no_divider_pairs.append((current_idx, current_idx + 1)) current_idx += 1 current_idx += 1 elif first_char == 'F': sizes.append(int(memory[1:])) # 转换为数值类型,避免绘图报错 colours.append(FREE_COLOUR) current_idx += 1 # 转换为浮点数组,计算角度 sizes = np.array(sizes, dtype=float) angle_percent = sizes / sizes.sum() * 360 # 生成每个扇区的起始角度(包含初始0度) angles = np.insert(np.cumsum(angle_percent), 0, 0) # 绘制饼图,先移除所有默认边缘线 wedges, _ = plt.pie(sizes, colors=colours, wedgeprops={'edgecolor': 'none'}) # 绘制需要保留的分隔线 ax = plt.gca() radius = 1 # 饼图默认半径为1 for i in range(len(angles)-1): # 检查当前分隔线是否属于需要跳过的对 skip = False for pair in no_divider_pairs: if pair[0] == i or pair[1] == i: skip = True break if skip: continue # 角度转弧度,计算线的端点坐标 theta = angles[i] * np.pi / 180 line = plt.Line2D([0, radius*np.cos(theta)], [0, radius*np.sin(theta)], color='black', linewidth=0.5) ax.add_line(line) # 绘制饼图外轮廓 circle = plt.Circle((0, 0), radius, fill=False, edgecolor='black', linewidth=0.5) ax.add_patch(circle) plt.axis("equal") plt.title("Memory Layout") plt.show()
代码说明
- 数据标记:新增
no_divider_pairs列表,精准记录不需要分隔线的扇区组合 - 角度计算:通过扇区占比计算每个扇区的起始角度,为绘制分隔线提供坐标依据
- 按需画线:使用
Line2D从圆心到边缘绘制分隔线,跳过标记的无分隔线位置,最后补充外轮廓保证整体视觉完整
内容的提问来源于stack exchange,提问作者Wzrd
相关产品推荐
相关产品推荐

