问题
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 ax3
are 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