How to make relim() and autoscale() in a scatter plot

雨燕双飞 提交于 2019-12-20 01:49:28

问题


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


回答1:


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)



回答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.



来源:https://stackoverflow.com/questions/51323505/how-to-make-relim-and-autoscale-in-a-scatter-plot

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!