【问题标题】:How to plot horizontal lines with text (i.e. a label) on a polar coordinates matplotlib plot? (Python)如何在极坐标 matplotlib 图上绘制带有文本(即标签)的水平线? (Python)
【发布时间】:2020-06-25 21:18:29
【问题描述】:

我正在尝试在极坐标图中标记节点。有 3 个“轴”被分割,我已经弄清楚如何使用象限来选择要标记的节点。但是,我不知道如何在绘图边缘对齐这些(即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])

【问题讨论】:

    标签: python matplotlib label polar-coordinates cartesian-coordinates


    【解决方案1】:

    您可以使用annotate 代替text,这样您就可以独立于点坐标指定文本坐标和文本坐标系。我们将文本放置在图形坐标中(01,详见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'
            )
    

    【讨论】:

    • 太棒了!这比我最终做的要好得多。我做了一个额外的 matplotlib 笛卡尔坐标轴并在上面绘制。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2016-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-18
    • 2017-05-18
    相关资源
    最近更新 更多