You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 01:29:55