6

The next code plots three subplots.

from ipywidgets import widgets
from IPython.display import display
import matplotlib.pyplot as plt
import numpy as np
%matplotlib notebook
fig, (ax1, ax2,ax3) = plt.subplots(nrows=3, figsize=(10,9))
line1, = ax1.semilogx([],[], label='Multipath')
hline1 = ax1.axhline(y = 0, linewidth=1.2, color='black',ls='--')
text1 = ax1.text(0, 0, "T Threshold",
                verticalalignment='top', horizontalalignment='left',
                transform=ax1.get_yaxis_transform(),
                color='brown', fontsize=10)
#ax1.set_xlabel('Separation Distance, r (m)')
ax1.set_ylabel('Received Power, $P_t$ (dBm)')
ax1.grid(True,which="both",ls=":")
ax1.legend()

line2, = ax2.semilogx([],[], label='Monostatic Link')
hline2 = ax2.axhline(y = 0, linewidth=1.2, color='black',ls='--')
text2 = ax2.text(0, 0, "R Threshold",
                verticalalignment='top', horizontalalignment='left',
                transform=ax2.get_yaxis_transform(),
                color='brown', fontsize=10)
#ax2.set_xlabel('Separation Distance, r (m)')
ax2.set_ylabel('Received Power, $P_t$ (dBm)')
ax2.grid(True,which="both",ls=":")
ax2.legend()

#line3, = ax3.semilogx([],[])
line3 = ax3.scatter([],[],  c='blue', alpha=0.75, edgecolors='none', s=6)
ax3.set_xlabel('Separation Distance, r (m)')
ax3.set_ylabel('Probability of error')
ax3.grid(True,which="both",ls=":")
ax3.set_xscale('log')
#ax3.set_xlim((0.55,13.5))
ax3.set_ylim((0,1))


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 )
    line1.set_data(r,y)

    hline1.set_ydata(-18)
    text1.set_position((0.02, -18.8))
    ax1.relim()
    ax1.autoscale_view()

    ######################################
    rd =np.sqrt((h1-h2)**2 + D**2)
    rd = np.sort(rd)
    P_r=0.8
    G_r=5 # 7dBi
    q_e_rcn2 = 1
    N_2 = np.random.normal(0, sigma*2, D.shape)
    rnd_2 = 10**(-N_2/10)
    F_2 = 126 
    y = 10*np.log10(  1000*(P_r*(G_r*1.622)**2*(lmb)**4*0.5**2*0.25)/((4*np.pi*rd)**4*1.2**2*1**2*F_2)*
            q_e**2*rnd*rnd_2*q_e_rcn1*q_e_rcn2  )
    line2.set_data(rd,y)
    hline2.set_ydata(-80)
    text2.set_position((0.02, -80.8))
    ax2.relim()
    ax2.autoscale_view()

    #######################################
    P_r = y
    SNR = P_r - ( 20 + 10*np.log10(1.6*10**6)-174 )
    CIR = P_r -( -100)
    SNR_linear = 10**(SNR/10)
    CIR_linear = (10**(CIR/10))/1000
    SNIR = 1/( 1/SNR_linear + 1/CIR_linear )
    K_dB = 3
    K = 10**(K_dB/10)
    BER = (1+K)/(2+2*K + SNIR)*np.exp(-3*SNIR/(2+K+SNIR))
    prob_error = 1-((1-BER )**6)
    #line3.set_data(rd,prob_error)
    line3.set_offsets(np.c_[rd,prob_error])
    ax3.relim()
    ax3.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)

Subplots 1st and 2nd change their axis limits with variations of the input parameters R_Height and T_Height. However, subplot 3rd does not make the relim() and autoscale() of the plot.

Is there any way to change the limits of the x-axis in a similar way of subplots 1st and 2nd?.

Regards

user1993416
  • 698
  • 1
  • 9
  • 28

2 Answers2

12

Both .relim() and .autoscale_view() do not take effect when the axes bounds have previously been set via .set_ylim(). So .set_ylim() needs to be removed from the code.

In addition updating the limits of a scatter plot (which is a matplotlib.collections.PathCollection) is a bit more complicated than for other plots.

You would first need to update the datalimits of the axes before calling autoscale_view(), because .relim() does not work with collections.

ax.ignore_existing_data_limits = True
ax.update_datalim(scatter.get_datalim(ax.transData))
ax.autoscale_view()

Here is a minimal reproducible example:

from ipywidgets import widgets
from IPython.display import display
import matplotlib.pyplot as plt
import numpy as np
%matplotlib notebook

x = np.arange(10)

fig, ax = plt.subplots()
scatter = ax.scatter(x,x, label="y = a*x+b")

ax.legend()

def update_plot(a, b):
    y = a*x+b
    scatter.set_offsets(np.c_[x,y])

    ax.ignore_existing_data_limits = True
    ax.update_datalim(scatter.get_datalim(ax.transData))
    ax.autoscale_view()

    fig.canvas.draw_idle()

a = widgets.FloatSlider(min=0.5, max=4, value=1, description= 'a:')
b = widgets.FloatSlider(min=0, max=40, value=10, description= 'b:')
widgets.interactive(update_plot, a=a, b=b)
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
2

As written in the documentation for Axes.relim(), Collections (which is the type returned by scatter()) are not supported at the moment.

Therefore you have to ajust the limits manually, something like

(...)
line3.set_offsets(np.c_[rd,prob_error])
ax3.set_xlim((min(rd),max(rd)))
ax3.set_ylim((min(prob_error),max(prob_error)))

It seems to me that all your plot share the same x values, though? If that's the case, you might want to use fig, (ax1, ax2,ax3) = plt.subplots((...), sharex=True). You will still have to set the ylim for ax3 by hand, but at least your x-axes will be the same across all subplots.

EDIT: I realize now that it looks like your data in ax3are bound between [0-1], and that you probably don't need to change the ylim() and that sharing the x-axis with the other subplots should be enough.

Diziet Asahi
  • 38,379
  • 7
  • 60
  • 75