如何在Matplotlib中实现特定弯曲线箭头样式?
在Matplotlib极坐标环形图中实现弯曲线注释箭头
问题描述
我正在复刻一张带有弯曲线注释箭头的极坐标环形对比图,已完成主体绘图代码(如下),目前已生成对应极坐标图,但无法实现示例中的弯曲线注释箭头样式,尝试使用'-('箭头样式未成功。现咨询:是否需要自定义arrowstyle(求相关学习资源),或存在更简便的实现方式?
import matplotlib.pyplot as plt from matplotlib.lines import Line2D import numpy as np import pandas as pd colors = ["#CC5A43","#CC5A43","#2C324F"] data = { "year": [2004, 2022, 2004, 2022, 2004, 2022], "countries" : [ "Denmark", "Denmark", "Norway", "Norway","Sweden", "Sweden",], "sites": [4,10,5,8,13,15] } df= pd.DataFrame(data) df = df.sort_values(['countries' ,'year' ], ascending=True ).reset_index(drop=True) df['ctry_code'] = df.countries.astype(str).str[:2].astype(str).str.upper() df['year_lbl'] ="'"+df['year'].astype(str).str[-2:].astype(str) df['sub_total'] = df.groupby('year')['sites'].transform('sum') no_bars = df.sub_total.max() sub_totals = df.sub_total.unique() years= df.year.unique() fig, axes = plt.subplots(nrows=2, ncols=1,figsize=(6, 6), subplot_kw=dict(polar=True)) fig.tight_layout(pad=3.0) colors = [["#CC5A43"]*4 +["#2C324F"]*5 + ["#5375D4"]*13, ["#CC5A43"]*10 +["#2C324F"]*8 + ["#5375D4"]*15] for sub_total, year,color,ax in zip( sub_totals, years,colors,axes.ravel()): angles = np.arange(0,2*np.pi,2*np.pi/sub_total) ax.plot([angles, angles],[0,1],lw=4, c="#CC5A43") ax.set_rorigin(-4) ax.set_theta_zero_location("N") ax.set_yticklabels([]) ax.set_xticklabels([]) ax.grid(False) ax.spines[['polar','inner']].set_color('w') ax.text(np.pi/2,-3.2, year,va="center" ) for i, j in enumerate(ax.lines): j.set_color(color[i]) #add legend color_legend = [ "#A54836", "#5375D4", "#2B314D",] lines = [Line2D([0], [0], color=c, linestyle='-', lw=4,) for c in color_legend] labels = df.countries.unique().tolist() plt.legend(lines, labels, bbox_to_anchor=(1.5, -0.02), loc="lower center", frameon=False, fontsize= 10)
解决方案
一、简便实现:用FancyArrowPatch绘制弯曲箭头
FancyArrowPatch支持通过贝塞尔曲线创建弯曲箭头,极坐标下需先转换为笛卡尔坐标:
from matplotlib.patches import FancyArrowPatch # 以第一个子图为例添加弯曲箭头 ax = axes[0] # 定义箭头起点和终点的极坐标(角度,半径) start_theta = np.pi/4 start_r = 1.2 end_theta = 3*np.pi/4 end_r = 1.2 # 转换为笛卡尔坐标 start_x = start_r * np.cos(start_theta) start_y = start_r * np.sin(start_theta) end_x = end_r * np.cos(end_theta) end_y = end_r * np.sin(end_theta) # 创建弯曲箭头:connectionstyle的rad参数控制弯曲程度,正负值改变弯曲方向 arrow = FancyArrowPatch( (start_x, start_y), (end_x, end_y), connectionstyle="arc3,rad=0.4", arrowstyle="->", lw=1.5, color="#333333" ) ax.add_patch(arrow)
二、自定义arrowstyle(复用特定样式)
如果需要重复使用相同样式的弯曲箭头,可以自定义ArrowStyle:
from matplotlib.patches import ArrowStyle class CustomCurvedArrow(ArrowStyle.SimpleArrow): def __init__(self, head_width=0.4, head_length=0.6, tail_width=0.2, rad=0.3): super().__init__(head_width, head_length, tail_width) self.rad = rad def func(self, path, coordsA, coordsB, mutation_size): x1, y1 = coordsA x2, y2 = coordsB # 计算贝塞尔曲线控制点,生成弯曲路径 cx = (x1 + x2)/2 + self.rad*(y2 - y1) cy = (y1 + y2)/2 + self.rad*(x1 - x2) path.moveTo(x1, y1) path.curveTo(cx, cy, cx, cy, x2, y2) # 添加箭头头部 self.add_head(path, x2, y2, x1, y1, mutation_size) # 使用自定义样式 arrow = FancyArrowPatch( (start_x, start_y), (end_x, end_y), arrowstyle=CustomCurvedArrow(rad=0.5), lw=1.5, color="#333333" ) ax.add_patch(arrow)
三、学习资源
- 直接查阅Matplotlib内置文档:运行
help(ArrowStyle)或help(FancyArrowPatch),获取参数说明和基础示例。 - 参考Matplotlib官方示例库:搜索"curved arrow"或"polar coordinate arrow"相关案例,学习实操技巧。
内容的提问来源于stack exchange,提问作者ruthpozuelo
相关产品推荐
相关产品推荐

