问题
I would like to put some text below a horizontal line in a plot that changes with changing two parameters. I use a function to plot and python widgets to set two controls that change the plot and at the same time change the distances of the x and y-axis.
My iPhyton code is:
from ipywidgets import widgets
from IPython.display import display
import matplotlib.pyplot as plt
import numpy as np
%matplotlib notebook
def update_plot(h1, h2):
plt.figure( figsize = (9,4) )
ax=plt.subplot(111)
D = np.arange(0.5, 12.0, 0.0100)
r = np.sqrt((h1-h2)**2 + D**2)
freq = 865.7 #freq = 915 MHz
lmb = 300/freq
H = D**2/(D**2+2*h1*h2)
theta = 4*np.pi*h1*h2/(lmb*D)
q_e = H**2*(np.sin(theta))**2 + (1 - H*np.cos(theta))**2
q_e_rcn1 = 1
P_x_G = 4 # 4 Watt EIRP
sigma = 1.94
N_1 = np.random.normal(0,sigma,D.shape)
rnd = 10**(-N_1/10)
F = 10 #
plt.semilogx(r,10*np.log10( 1000*(P_x_G*1.622*((lmb)**2) *0.5*1) / (((4*np.pi*r)**2) *1.2*1*F)*q_e*rnd*q_e_rcn1 ),
label='Multipath')
plt.axhline(y = -18, linewidth=1.2, color='black',ls='--')
#plt.text(0.60, -22, r"T Threshold")
ax.text(0.02, 0.37, "T Threshold",
verticalalignment='bottom', horizontalalignment='left',
transform=ax.transAxes,
color='brown', fontsize=10)
plt.xlabel('Separation Distance, r (m)')
plt.ylabel('Received Power, $P_t$ (dBm)')
plt.grid(True,which="both",ls=":")
plt.legend()
#####################
r_height = widgets.FloatSlider(min=0.5, max=4, value=0.9, description= 'R_Height:')
t_height = widgets.FloatSlider(min=0.15, max=1.5, value=0.5, description= 'T_Height:')
widgets.interactive(update_plot, h1=r_height, h2=t_height)
Matplotlib documentation in "Text in Matplotlib Plots" indicates different ways to place text into the plot. I have tried two forms: the set of the exact position into the plot (commented) and the relative position. The first way place the text close to the horizontal line but goes outside the frame when it changes the plot with the controls. The second way to keep the text at a distance from the y-axis but does not change as the horizontal line goes up and down when the parameters change.
I would like to find a way to put the text close to the horizontal line but at a certain distance of the y=0 axis.
Regards
回答1:
I think you would want to position the text in a blended coordinate system where the x coordinate is the axes coordinate system and the y coordinate is the data coordinate system. Such blended coordinate systems is conveniently already available through the ax.get_yaxis_transform(). E.g. to place the text 2% away from the y axis, at a y coordinate of -18,
ax.text(0.02, -18, "T Threshold", transform=ax.get_yaxis_transform() )
In general you may want to create the plot only once, and then using the sliders, only update the relevant parts of it. In total this would look like:
from ipywidgets import widgets
from IPython.display import display
import matplotlib.pyplot as plt
import numpy as np
%matplotlib notebook
import ipywidgets
fig, ax = plt.subplots()
line, = ax.semilogx([],[], label='Multipath')
hline = ax.axhline(y = 0, linewidth=1.2, color='black',ls='--')
text = ax.text(0, 0, "T Threshold",
verticalalignment='top', horizontalalignment='left',
transform=ax.get_yaxis_transform(),
color='brown', fontsize=10)
ax.set_xlabel('Separation Distance, r (m)')
ax.set_ylabel('Received Power, $P_t$ (dBm)')
ax.grid(True,which="both",ls=":")
ax.legend()
def update_plot(h1, h2):
D = np.arange(0.5, 12.0, 0.0100)
r = np.sqrt((h1-h2)**2 + D**2)
freq = 865.7 #freq = 915 MHz
lmb = 300/freq
H = D**2/(D**2+2*h1*h2)
theta = 4*np.pi*h1*h2/(lmb*D)
q_e = H**2*(np.sin(theta))**2 + (1 - H*np.cos(theta))**2
q_e_rcn1 = 1
P_x_G = 4 # 4 Watt EIRP
sigma = 1.94
N_1 = np.random.normal(0,sigma,D.shape)
rnd = 10**(-N_1/10)
F = 10
y = 10*np.log10( 1000*(P_x_G*1.622*((lmb)**2) *0.5*1) / (((4*np.pi*r)**2) *1.2*1*F)*q_e*rnd*q_e_rcn1 )
line.set_data(r,y)
hline.set_ydata(-18)
text.set_position((0.02, -18.5))
ax.relim()
ax.autoscale_view()
fig.canvas.draw_idle()
r_height = widgets.FloatSlider(min=0.5, max=4, value=0.9, description= 'R_Height:')
t_height = widgets.FloatSlider(min=0.15, max=1.5, value=0.5, description= 'T_Height:')
widgets.interactive(update_plot, h1=r_height, h2=t_height)
Note that the sliders will now appear below the plot, as they are created afterwards - this might a bearable downside given the advantages of a much better responsivity of the slider changes due to not recreating the complete plot at each slider movement.
来源:https://stackoverflow.com/questions/51266355/how-to-keep-some-text-relative-to-the-line-into-the-plot-when-the-plot-changes