有没有方便的方法在matplotlib中为绘图添加缩放指示符?

时间:2017-04-06 14:51:22

标签: matplotlib annotations

我想在下面的(其他)空图中添加一个比例指示器,如标题为“10kpc”的图表。基本上,轴使用一个度量单位,我想用不同的单位表示图中的长度。它必须具有与下面相同的样式,即| ---- |酒吧上面有文字。

在matplotlib中有没有方便的方法来做或者我必须画三条线(两条小垂直,一条水平)并添加文字?一个理想的解决方案甚至不需要我在数据维度中设置坐标,即我只是在horizontalalignment='left', verticalalignment='bottom', transform=ax.transAxes的行上说一些东西,并且只指定数据坐标中的宽度。

我与annotate()arrow()以及他们的文件进行了一段时间的争吵,直到我得出结论,它们并不完全有用,但我可能错了。

修改

下面的代码是最接近的,我到目前为止。我仍然不喜欢在数据坐标系中指定x坐标。我想在数据中指定的唯一内容是条的宽度。其余的应该放在绘图系统中,理想情况下应该相对于文本放置条形图(上面几个像素)。

import matplotlib.pyplot as plt 
import matplotlib.transforms as tfrms
plt.imshow(somedata)
plt.colorbar()
ax = plt.gca()
trans = tfrms.blended_transform_factory( ax.transData, ax.transAxes )
plt.errorbar( 5, 0.06, xerr=10*arcsecperkpc/2, color='k', capsize=5, transform=trans )
plt.text( 5, 0.05, '10kpc',  horizontalalignment='center', verticalalignment='top', transform=trans )

Empty plot with scale indicator

1 个答案:

答案 0 :(得分:2)

这是一个代码,用于向绘图添加水平比例尺(或比例指示器或比例尺)。条的宽度以数据单位给出,而边的高度以轴单位的分数表示。

该解决方案基于AnchoredOffsetbox,其中包含VPackerVPacker的下排有一个标签,上排有一个AuxTransformBox 这里的关键是AnchoredOffsetbox相对于轴定位,使用类似于图例定位的loc参数(例如loc=4表示右下角)。但是,AuxTransformBox包含一组元素,这些元素使用转换位于框内。作为变换,我们可以根据轴变换选择根据轴和y坐标的数据变换来变换x坐标的混合变换。执行此操作的转换实际上是轴本身的xaxis_transform。将此变换提供给AuxTransformBox允许我们以有用的方式指定其中的艺术家(在这种情况下为Line2D),例如栏的行将是Line2D([0,size],[0,0])

所有这些都可以打包到一个类中,对AnchoredOffsetbox进行子类化,这样就很容易在现有代码中使用。

import matplotlib.pyplot as plt
import matplotlib.offsetbox
from matplotlib.lines import Line2D
import numpy as np; np.random.seed(42)

x = np.linspace(-6,6, num=100)
y = np.linspace(-10,10, num=100)
X,Y = np.meshgrid(x,y)
Z = np.sin(X)/X+np.sin(Y)/Y

fig, ax = plt.subplots()
ax.contourf(X,Y,Z, alpha=.1)
ax.contour(X,Y,Z, alpha=.4)

class AnchoredHScaleBar(matplotlib.offsetbox.AnchoredOffsetbox):
    """ size: length of bar in data units
        extent : height of bar ends in axes units """
    def __init__(self, size=1, extent = 0.03, label="", loc=2, ax=None,
                 pad=0.4, borderpad=0.5, ppad = 0, sep=2, prop=None, 
                 frameon=True, **kwargs):
        if not ax:
            ax = plt.gca()
        trans = ax.get_xaxis_transform()
        size_bar = matplotlib.offsetbox.AuxTransformBox(trans)
        line = Line2D([0,size],[0,0], **kwargs)
        vline1 = Line2D([0,0],[-extent/2.,extent/2.], **kwargs)
        vline2 = Line2D([size,size],[-extent/2.,extent/2.], **kwargs)
        size_bar.add_artist(line)
        size_bar.add_artist(vline1)
        size_bar.add_artist(vline2)
        txt = matplotlib.offsetbox.TextArea(label, minimumdescent=False)
        self.vpac = matplotlib.offsetbox.VPacker(children=[size_bar,txt],  
                                 align="center", pad=ppad, sep=sep) 
        matplotlib.offsetbox.AnchoredOffsetbox.__init__(self, loc, pad=pad, 
                 borderpad=borderpad, child=self.vpac, prop=prop, frameon=frameon)

ob = AnchoredHScaleBar(size=3, label="3 units", loc=4, frameon=True,
                       pad=0.6,sep=4,color="crimson") 
ax.add_artist(ob)
plt.show()

enter image description here

为了在问题中获得所需的结果,您可以关闭帧并调整线宽。当然,从想要显示的单位(kpc)到数据单位(km?)的转换需要由你自己完成。

ikpc = lambda x: x*3.085e16 #x in kpc, return in km
ob = AnchoredHScaleBar(size=ikpc(10), label="10kpc", loc=4, frameon=False,
                       pad=0.6,sep=4,color="k", linewidth=0.8) 

enter image description here