如何在极坐标 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) 行的末尾,我想添加
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'
)
我正在尝试在我的极坐标图中标记节点。有 3 个“轴”被拆分,我已经弄清楚如何使用象限来 select 标记哪些节点。但是,我无法弄清楚如何将它们对齐到绘图的边缘(即 axis_maximum
)。我花了几个小时试图弄清楚这一点。我最好的选择是在左边或右边用 .
填充,但这是一个固定数字,当点太多时会变得混乱。此外,当有很多要点时,这种方法超出了情节的“循环”性质。 I did some trigonometry 计算出所有内容的长度,但这很难使用 .
.
如果有人能提供帮助,我们将不胜感激。我展示了下面的情节,然后用红色添加了我想要实现的内容。模拟图中的 label
对应于 for 循环中的 name_node
。理想情况下,我想避免使用像 .
这样的字符,而宁愿使用实际的 matplotlib
Line
对象,这样我就可以指定 linestyle
像 :
或 -
.
总而言之,我想做以下事情:
- 添加从我的“轴”延伸到绘图外缘的水平线(根据象限向右或向左延伸)
- 在 (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'
)