如何在极坐标 matplotlib 图上绘制带有文本(即标签)的水平线? (Python)

How to plot horizontal lines with text (i.e. a label) on a polar coordinates matplotlib plot? (Python)

我正在尝试在我的极坐标图中标记节点。有 3 个“轴”被拆分,我已经弄清楚如何使用象限来 select 标记哪些节点。但是,我无法弄清楚如何将它们对齐到绘图的边缘(即 axis_maximum)。我花了几个小时试图弄清楚这一点。我最好的选择是在左边或右边用 . 填充,但这是一个固定数字,当点太多时会变得混乱。此外,当有很多要点时,这种方法超出了情节的“循环”性质。 I did some trigonometry 计算出所有内容的长度,但这很难使用 ..

等文本单位来实现

如果有人能提供帮助,我们将不胜感激。我展示了下面的情节,然后用红色添加了我想要实现的内容。模拟图中的 label 对应于 for 循环中的 name_node。理想情况下,我想避免使用像 . 这样的字符,而宁愿使用实际的 matplotlib Line 对象,这样我就可以指定 linestyle:-.

总而言之,我想做以下事情:

  1. 添加从我的“轴”延伸到绘图外缘的水平线(根据象限向右或向左延伸)
  2. 在 (1) 行的末尾,我想添加 name_node 文本。

编辑:


import numpy as np
from numpy import array # I don't like this but it's for loading in the pd.DataFrame
import pandas as pd 
import matplotlib.pyplot as plt
df = pd.DataFrame({'node_positions_normalized': {'iris_100': 200.0, 'iris_101': 600.0, 'iris_102': 1000.0, 'iris_0': 200.0, 'iris_1': 600.0, 'iris_2': 1000.0, 'iris_50': 200.0, 'iris_51': 600.0, 'iris_52': 1000.0}, 'theta': {'iris_100': array([5.42070629, 6.09846678]), 'iris_101': array([5.42070629, 6.09846678]), 'iris_102': array([5.42070629, 6.09846678]), 'iris_0': array([1.23191608, 1.90967657]), 'iris_1': array([1.23191608, 1.90967657]), 'iris_2': array([1.23191608, 1.90967657]), 'iris_50': array([3.32631118, 4.00407168]), 'iris_51': array([3.32631118, 4.00407168]), 'iris_52': array([3.32631118, 4.00407168])}})
axis_maximum = df["node_positions_normalized"].max()
thetas = np.unique(np.stack(df["theta"].values).ravel())


def pol2cart(rho, phi):
    x = rho * np.cos(phi)
    y = rho * np.sin(phi)
    return(x, y)

def _get_quadrant_info(theta_representative):
    # 0/360
    if theta_representative == np.deg2rad(0):
        quadrant = 0
    # 90
    if theta_representative == np.deg2rad(90):
        quadrant = 90
    # 180
    if theta_representative == np.deg2rad(180):
        quadrant = 180
    # 270
    if theta_representative == np.deg2rad(270):
        quadrant = 270

    # Quadrant 1
    if np.deg2rad(0) < theta_representative < np.deg2rad(90):
        quadrant = 1
    # Quadrant 2
    if np.deg2rad(90) < theta_representative < np.deg2rad(180):
        quadrant = 2
    # Quadrant 3
    if np.deg2rad(180) < theta_representative < np.deg2rad(270):
        quadrant = 3
    # Quadrant 4
    if np.deg2rad(270) < theta_representative < np.deg2rad(360):
        quadrant = 4
    return quadrant
    
    
with plt.style.context("seaborn-white"):
    fig = plt.figure(figsize=(8,8))
    ax = plt.subplot(111, polar=True)
    ax_cartesian = fig.add_axes(ax.get_position(), frameon=False, polar=False)
    ax_cartesian.set_xlim(-axis_maximum, axis_maximum)
    ax_cartesian.set_ylim(-axis_maximum, axis_maximum)

    # Draw axes
    for theta in thetas:
        ax.plot([theta,theta], [0,axis_maximum], color="black")
        
    # Draw nodes
    for name_node, data in df.iterrows():
        r = data["node_positions_normalized"]
        for theta in data["theta"]:
            ax.scatter(theta, r, color="teal", s=150, edgecolor="black", linewidth=1, alpha=0.618)
        # Draw node labels
        quadrant = _get_quadrant_info(np.mean(data["theta"]))
 
        # pad on the right and push label to left
        if quadrant in {1,4}:
            theta_anchor_padding = min(data["theta"])
        # pad on left and push label to the right
        if quadrant in {2,3}:
            theta_anchor_padding = max(data["theta"])
            
        # Plot
        ax.text(
            s=name_node,
            x=theta_anchor_padding,
            y=r,
            horizontalalignment="center",
            verticalalignment="center",
        )
    
    ax.set_rlim((0,axis_maximum))
    
    # Convert polar to cartesian and plot on cartesian overlay?
    xf, yf = pol2cart(theta_anchor_padding, r) #fig.transFigure.inverted().transform(ax.transData.transform((theta_anchor_padding, r)))
    ax_cartesian.plot([xf, axis_maximum], [yf, yf])

您可以使用 annotate instead of text, this allows you to specify the text coordinates and the text coordinate system independently of the point coordinates. We place the text in figure coordinates (0 to 1, see here 了解详情)。在 设置 r 限制后,获得从数据到图形坐标的转换很重要。

with plt.style.context("seaborn-white"):
    fig = plt.figure(figsize=(8,8))
    ax = plt.subplot(111, polar=True)
    ax.set_rlim((0,axis_maximum))
    ann_transf = ax.transData + fig.transFigure.inverted() 

    # Draw axes
    for theta in thetas:
        ax.plot([theta,theta], [0,axis_maximum], color="black")
    
    
    # Draw nodes
    for name_node, data in df.iterrows():
        r = data["node_positions_normalized"]
        for theta in data["theta"]:
            ax.scatter(theta, r, color="teal", s=150, edgecolor="black", linewidth=1, alpha=0.618)
        # Draw node labels
        quadrant = _get_quadrant_info(np.mean(data["theta"]))
 
        # pad on the right and push label to left
        if quadrant in {1,4}:
            theta_anchor_padding = min(data["theta"])
        # pad on left and push label to the right
        if quadrant in {2,3}:
            theta_anchor_padding = max(data["theta"])
            
        # Plot
        _,y = ann_transf.transform((theta_anchor_padding, r))
        ax.annotate(name_node, 
                    (theta_anchor_padding,r), 
                    (0.91 if quadrant in {1,4} else 0.01, y),
                    textcoords='figure fraction',
                    arrowprops=dict(arrowstyle='-', color='r'),
                    color='r',
                    verticalalignment='center'
        )