【问题标题】:Matplotlib: different scale on negative side of the axisMatplotlib:轴负侧的不同比例
【发布时间】:2019-05-10 23:59:21
【问题描述】:

背景


我试图在一个图上显示三个变量。我已经根据其他一些变量使用不同颜色的线连接了三个点。这里显示


问题


我想要做的是在负 x 轴上有一个不同的比例。这将帮助我提供正的 x_ticks、不同的轴标签以及图像左侧线条的清晰和整洁的表示


问题


  • 如何从 0 开始向负方向设置不同的正 x 轴?
  • 根据沿该方向绘制的数据生成 xticks
  • 为这个新轴设置一个单独的 xlabel

其他信息


我检查了有关包含多个轴的其他问题,例如thisthis。然而,这些问题并没有达到目的。

使用的代码

font_size = 20
plt.rcParams.update({'font.size': font_size})

fig = plt.figure()
ax = fig.add_subplot(111)
#read my_data from file or create it

for case in my_data:

    #Iterating over my_data

    if condition1 == True:
        local_linestyle = '-'
        local_color = 'r'
        local_line_alpha = 0.6
    elif condition2 == 1:
        local_linestyle = '-'
        local_color = 'b'
        local_line_alpha = 0.6
    else:
        local_linestyle = '--'
        local_color = 'g'
        local_line_alpha = 0.6

    datapoint = [case[0], case[1], case[2]]

    plt.plot(datapoint[0], 0, color=local_color)
    plt.plot(-datapoint[2], 0, color=local_color)
    plt.plot(0, datapoint[1], color=local_color)
    plt.plot([datapoint[0], 0], [0, datapoint[1]], linestyle=local_linestyle, color=local_color)
    plt.plot([-datapoint[2], 0], [0, datapoint[1]], linestyle=local_linestyle, color=local_color)
plt.show()
exit()

【问题讨论】:

  • 您的示例图片很好。你能把你用来制作它的代码贴出来吗?
  • 更新了问题以包含使用的代码。

标签: matplotlib multiple-axes


【解决方案1】:

如其他答案中所示,派生mscale.ScaleBase 类的方法可能对您的目的来说太复杂了。 您可以将两个比例变换函数传递给set_xscaleset_yscale,如下所示。

def get_scale(a=1):  # a is the scale of your negative axis
    def forward(x):
        x = (x >= 0) * x + (x < 0) * x * a
        return x

    def inverse(x):
        x = (x >= 0) * x + (x < 0) * x / a
        return x

    return forward, inverse

fig, ax = plt.subplots()

forward, inverse = get_scale(a=3)
ax.set_xscale('function', functions=(forward, inverse))  # this is for setting x axis

# do plotting

更多示例请见this doc

【讨论】:

    【解决方案2】:

    您可以定义自定义比例,其中低于零的值与高于零的值的比例不同。

    import numpy as np
    from matplotlib import scale as mscale
    from matplotlib import transforms as mtransforms
    from matplotlib.ticker import FuncFormatter
    
    class AsymScale(mscale.ScaleBase):
        name = 'asym'
    
        def __init__(self, axis, **kwargs):
            mscale.ScaleBase.__init__(self)
            self.a = kwargs.get("a", 1)
    
        def get_transform(self):
            return self.AsymTrans(self.a)
    
        def set_default_locators_and_formatters(self, axis):
            # possibly, set a different locator and formatter here.
            fmt = lambda x,pos: "{}".format(np.abs(x))
            axis.set_major_formatter(FuncFormatter(fmt))
    
        class AsymTrans(mtransforms.Transform):
            input_dims = 1
            output_dims = 1
            is_separable = True
    
            def __init__(self, a):
                mtransforms.Transform.__init__(self)
                self.a = a
    
            def transform_non_affine(self, x):
                return (x >= 0)*x + (x < 0)*x*self.a
    
            def inverted(self):
                return AsymScale.InvertedAsymTrans(self.a)
    
        class InvertedAsymTrans(AsymTrans):
    
            def transform_non_affine(self, x):
                return (x >= 0)*x + (x < 0)*x/self.a
            def inverted(self):
                return AsymScale.AsymTrans(self.a)
    

    使用它,您将提供一个缩放参数a,用于缩放轴的负值部分。

    # Now that the Scale class has been defined, it must be registered so
    # that ``matplotlib`` can find it.
    mscale.register_scale(AsymScale)
    
    import matplotlib.pyplot as plt
    fig, ax = plt.subplots()
    
    ax.plot([-2, 0, 5], [0,1,0])
    ax.set_xscale("asym", a=2)
    
    ax.annotate("negative axis", xy=(.25,0), xytext=(0,-30), 
                xycoords = "axes fraction", textcoords="offset points", ha="center")
    ax.annotate("positive axis", xy=(.75,0), xytext=(0,-30), 
                xycoords = "axes fraction", textcoords="offset points", ha="center")
    plt.show()
    

    这个问题不是很清楚需要什么 xticks 和标签,所以我暂时省略了。

    【讨论】:

    • 我需要有正的 xticks 和标签,此外,对于 x 轴,我需要单独的 xlabels 一个正向和一个负向。
    • 这太坏了!我只是希望有一种更简单的方法。
    【解决方案3】:

    以下是获得您想要的东西的方法。此方案使用两个缠绕在一起的axes 对象,对原点左右进行不同的缩放,然后隐藏所有证据:

    import matplotlib.pyplot as plt
    import matplotlib as mpl
    from numbers import Number
    
    tickkwargs = {m+k:False for k in ('bottom','top','left','right') for m in ('','label')}
    
    p = np.zeros((10, 3, 2))
    p[:,0,0] -= np.arange(10)*.1 + .5
    p[:,1,1] += np.repeat(np.arange(5), 2)*.1 + .3
    p[:,2,0] += np.arange(10)*.5 + 2
    
    fig = plt.figure(figsize=(8,6))
    host = fig.add_subplot(111)
    par = host.twiny()
    
    host.set_xlim(-6, 6)
    par.set_xlim(-1, 1)
    
    for ps in p:
        # mask the points with negative x values
        ppos = ps[ps[:,0] >= 0].T
        host.plot(*ppos)
    
        # mask the points with positive x values
        pneg = ps[ps[:,0] <= 0].T
        par.plot(*pneg)
    
    # hide all possible ticks/notation text that could be set by the second x axis
    par.tick_params(axis="both", **tickkwargs)
    par.xaxis.get_offset_text().set_visible(False)
    
    # fix the x tick labels so they're all positive
    host.set_xticklabels(np.abs(host.get_xticks()))
    
    fig.show()
    

    输出:

    这是我在上面代码中使用的点集p 在正常绘制时的样子:

    fig = plt.figure(figsize=(8,6))
    ax = fig.gca()
    for ps in p:
        ax.plot(*ps.T)
    fig.show()
    

    输出:

    【讨论】:

    • 能否请您帮忙设置 x 轴标签。一个在正方向,一个在负方向,例如在 x 正数中说“foo”,在 x 负数中说“动物园”。
    猜你喜欢
    • 2012-02-24
    • 2019-04-20
    • 1970-01-01
    • 2015-01-20
    • 1970-01-01
    • 1970-01-01
    • 2017-09-03
    • 2018-10-08
    • 1970-01-01
    相关资源
    最近更新 更多