【问题标题】:define custom Normalisation function in matplotlib when using plt.colorbar()使用 plt.colorbar() 时在 matplotlib 中定义自定义归一化函数
【发布时间】:2016-02-09 14:52:10
【问题描述】:

要使用 matplotlib 颜色条,必须指定 matplotlib.cm.ScalarMappablematplotlib.colors.Normalize 的子类中的对象,colorbar 可以从中知道如何将数据标准化为 [0,1] 浮点值。

matplotlib 提供的归一化过程只有几个,线性归一化,对数,幂律等。但在实践中,我们可能希望使用自己编写的其他归一化函数。

我们可以使用任何函数将数据数组标准化为 [0,1],但如果没有使用 Nomalization 子类构建的 Scalarmappable,颜色条将不会正确显示刻度和标签。

我想知道我对 matplotlib colorbar 的理解是正确的还是有其他方法可以很容易地做到这一点?或者我们必须手动编写一个子类来包装自定义规范化函数?

【问题讨论】:

    标签: python matplotlib colors


    【解决方案1】:

    为此,您可以轻松地继承 matplotlib.colors.Normalize。这是我为previous SO question 编写的分段规范化类的示例:

    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.colors import Normalize
    
    class PiecewiseNorm(Normalize):
        def __init__(self, levels, clip=False):
            # input levels
            self._levels = np.sort(levels)
            # corresponding normalized values between 0 and 1
            self._normed = np.linspace(0, 1, len(levels))
            Normalize.__init__(self, None, None, clip)
    
        def __call__(self, value, clip=None):
            # linearly interpolate to get the normalized value
            return np.ma.masked_array(np.interp(value, self._levels, self._normed))
    
        def inverse(self, value):
            return 1.0 - self.__call__(value)
    

    例如:

    y, x = np.mgrid[0.0:3.0:100j, 0.0:5.0:100j]
    H = 50.0 * np.exp( -(x**2 + y**2) / 4.0 )
    levels = [0, 1, 2, 3, 6, 9, 20, 50]
    
    H1 = -50.0 * np.exp( -(x**2 + y**2) / 4.0 )
    levels1 = [-50, -20, -9, -6, -3, -2, -1, 0]
    
    fig, ax = plt.subplots(2, 2, gridspec_kw={'width_ratios':(20, 1), 'wspace':0.05})
    
    im0 = ax[0, 0].contourf(x, y, H, levels, cmap='jet', norm=PiecewiseNorm(levels))
    cb0 = fig.colorbar(im0, cax=ax[0, 1])
    im1 = ax[1, 0].contourf(x, y, H1, levels1, cmap='jet', norm=PiecewiseNorm(levels1))
    cb1 = fig.colorbar(im1, cax=ax[1, 1])
    
    plt.show()
    

    【讨论】:

    • 太棒了!你的例子只是展示了它是多么容易完成。我的意思是,matplotlib 文档有点懒惰,不检查源代码我不确定 call 和 inverse() 的意思。所以基本上只是用 call 包装规范化函数并谨慎行事。
    • 老实说,我并不完全确定 inverse 的实际用途是什么——我给出的示例不会在派生类中覆盖该方法,但无论如何定义它似乎更安全.
    • 通过查看源代码inverse(),在Normalization子类中定义了线性、幂律、对数等类型,将归一化值映射为其原始值,这似乎是方法名称的面值。该方法实际上只在ColorbarBase 的私有方法中调用。那里的文档和 cmets 很少,一个没有路标的陌生土地。
    【解决方案2】:

    感谢@ali_m 的想法,几天后我想我有了一个想法,即使用any 规范化函数y=func(x) 定义一个自定义Normalization 子类。基本上用任何func(self._levels) 给出的规范化值替换私有成员self._normed。并且在初始化子类时,必须将函数挂钩赋予规范化函数func。但请确保func 必须是真正的规范化。

    以下代码的灵感来自@ali_m 的回答:

    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib.colors import Normalize
    
    class CustomNorm(Normalize):
        def __init__(self, levels, func, clip=None):
            # input levels
            self._levels = np.linspace(min(levels), max(levels), 10)
            # corresponding normalized values between 0 and 1
            self._normed = func(self._levels)
            Normalize.__init__(self, None, None, clip)
    
        def __call__(self, value, clip=None):
            # linearly interpolate to get the normalized value
            return np.ma.masked_array(np.interp(value, self._levels, self._normed))
    
        def inverse(self, value):
            return 1.0 - self.__call__(value)
    
    def func(x):
        # whatever function, just normalise x into a sub-field of [0,1], 
        # it can be even [0,0.5]
        return x/50.0/2.0
    
    y, x = np.mgrid[0.0:3.0:100j, 0.0:5.0:100j]
    H = 50.0 * np.exp( -(x**2 + y**2) / 4.0 )
    levels = [0, 1, 2, 3, 6, 9, 20, 50]
    # levels = [0, 50]
    
    # H1 = -50.0 * np.exp( -(x**2 + y**2) / 4.0 )
    # levels1 = [-50, -20, -9, -6, -3, -2, -1, 0]
    # levels1 = [-50, 0]
    
    fig, ax = plt.subplots(2, 2, gridspec_kw={'width_ratios':(20, 1), 'wspace':0.05})
    
    im0 = ax[0, 0].contourf(x, y, H, cmap='jet', norm=CustomNorm(levels, func))
    cb0 = fig.colorbar(im0, cax=ax[0, 1])
    # im1 = ax[1, 0].contourf(x, y, H1, levels1, cmap='jet', norm=CustomNorm(levels1, func))
    # cb1 = fig.colorbar(im1, cax=ax[1, 1])
    
    plt.show()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-09-11
      • 1970-01-01
      • 1970-01-01
      • 2013-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-21
      相关资源
      最近更新 更多