如何在散点图中制作 relim() 和 autoscale()

Posted

技术标签:

【中文标题】如何在散点图中制作 relim() 和 autoscale()【英文标题】:How to make relim() and autoscale() in a scatter plot 【发布时间】:2018-12-21 17:44:43 【问题描述】:

接下来的代码绘制了三个子图。

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)

子图 1st 和 2nd 随着输入参数 R_Height 和 T_Height 的变化而改变它们的轴限制。但是,子图 3rd 并没有构成该图的 relim()autoscale()

有没有办法以类似于子图 1 和 2 的方式更改 x 轴的限制?

问候

【问题讨论】:

【参考方案1】:

.relim().autoscale_view() 在先前已通过 .set_ylim() 设置轴边界时都不会生效。所以.set_ylim()需要从代码中去掉。

此外,更新散点图(matplotlib.collections.PathCollection)的限制比其他图要复杂一些。

在调用autoscale_view() 之前,您首先需要更新坐标区的数据限制,因为.relim() 不适用于集合。

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

这是一个最小的可重现示例:

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)

【讨论】:

谢谢。解决方案效果很好,这个问题对我来说真的很困难。 你能解释一下,ax.ignore_existing_data_limits = True 是做什么的吗?【参考方案2】:

目前不支持written in the documentation for Axes.relim()Collectionsscatter()返回的类型)。

因此您必须手动调整限制,例如

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

在我看来,您的所有情节都共享相同的 x 值,但是?如果是这种情况,您可能需要使用fig, (ax1, ax2,ax3) = plt.subplots((...), sharex=True)。您仍然需要手动设置 ax3 的 ylim,但至少您的 x 轴在所有子图中都是相同的。

编辑:我现在意识到 ax3 中的数据似乎绑定在 [0-1] 之间,并且您可能不需要更改 ylim() 并且与其他子图应该足够了。

【讨论】:

以上是关于如何在散点图中制作 relim() 和 autoscale()的主要内容,如果未能解决你的问题,请参考以下文章

如何在散点图中自定义标记颜色和形状? [复制]

如何避免在散点图中重叠文本?

如何在散点图中显示图例以区分类

如何在散点图中绘制不同的周末天数?

如何在散点图中添加图像而不是点(python)[重复]

R语言如何在散点图中进行样本点标记?