1

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)

enter image description here

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

pablo
  • 385
  • 2
  • 14
  • I do not get any sliders when running your code (I use ipwidgets 7.2). – ImportanceOfBeingErnest Jul 10 '18 at 14:23
  • @ImportanceOfBeingErnest I had to install ipywidgets with `pip install ipywidgets` and run in a Terminal window `jupyter nbextension enable --py --sys-prefix widgetsnbextension`. I do not know if that is the problem. The code runs fine for me. I can upload an image of the plot. I am using Python 3. – pablo Jul 10 '18 at 14:35
  • @ImportanceOfBeingErnest Sometimes I have to move the sliders for the plot frame to appear. – pablo Jul 10 '18 at 14:42
  • @ImportanceOfBeingErnest Have you tried ipywidgets with another plot? It looks like ipywidgets is not working. – pablo Jul 10 '18 at 15:49
  • Yeah I don't know what really caused this, I created a new environment where this now works fine; so it's definitely something on my side, nothing you need to worry about. – ImportanceOfBeingErnest Jul 10 '18 at 17:31

1 Answers1

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)

enter image description here

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.

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712