Matplotlib 对称对数颜色图不以零为中心

Matplotlib Symmetric Logarithmic colormap not centered at zero

我正在研究一个问题,即从 matplotlib.colors 导入的默认颜色图 SymLogNorm 对于我的数据实际上并不对称。我的数据的均值和中值高于零,但我希望零值显示为白色。 SymLogNorm returns 中心值略高于零的颜色图 -- 微妙,但绝对引人注目。

这个问题有什么解决办法吗?

这是我的解决方法 - 它基本上对靠近中心的区域使用手动线性刻度,对更远的区域使用对数刻度。您可以像指定 SymLogNorm 一样指定 lin_thres 和其他参数。

class MidpointLogNorm(colors.SymLogNorm):
    """
    Normalise the colorbar so that diverging bars work there way either side from a prescribed midpoint value)
    e.g. im=ax1.imshow(array, norm=MidpointNormalize(midpoint=0.,vmin=-100, vmax=100))

    All arguments are the same as SymLogNorm, except for midpoint    
    """
    def __init__(self, lin_thres, lin_scale, midpoint=None, vmin=None, vmax=None):
        self.midpoint = midpoint
        self.lin_thres = lin_thres
        self.lin_scale = lin_scale
        #fraction of the cmap that the linear component occupies
        self.linear_proportion = (lin_scale / (lin_scale + 1)) * 0.5
        print(self.linear_proportion)

        colors.SymLogNorm.__init__(self, lin_thres, lin_scale, vmin, vmax)

    def __get_value__(self, v, log_val, clip=None):
        if v < -self.lin_thres or v > self.lin_thres:
            return log_val
        
        x = [-self.lin_thres, self.midpoint, self.lin_thres]
        y = [0.5 - self.linear_proportion, 0.5, 0.5 + self.linear_proportion]
        interpol = np.interp(v, x, y)
        return interpol

    def __call__(self, value, clip=None):
        log_val = colors.SymLogNorm.__call__(self, value)

        out = [0] * len(value)
        for i, v in enumerate(value):
            out[i] = self.__get_value__(v, log_val[i])

        return np.ma.masked_array(out)

我从这里以中点为中心画了灵感:http://chris35wills.github.io/matplotlib_diverging_colorbar/

您之前的回答对我来说并不适用,因为中点与原始中点“相距甚远”。问题是您的对数值中没有考虑中点。

我稍微更改了您的代码并通过创建 2 个 SymLogNorm 解决了该问题。两者都以中点为中心。一个从vmin开始,一个从vmax结束(极值按中点填充)。

如果我取一个值 v,我会这样分配颜色条坐标:

  • vmin < v < midpoint - lin_thresh,所以使用从 vmin

    开始的 SymLogNorm
  • 中点 - lin_thresh < v < 中点 + lin_thresh,所以使用插值

  • midpoint + lin_thresh < v < vmax,所以使用结束于 vmax

    的 SymLogNorm
    class MidpointLogNorm(colors.SymLogNorm):
      def __init__(self, lin_thres, lin_scale, midpoint=None, vmin=None, vmax=None):
          self.midpoint = midpoint
          self.lin_thres = lin_thres
          self.lin_scale = lin_scale
          #fraction of the cmap that the linear component occupies
          self.linear_proportion = (lin_scale / (lin_scale + 1)) * 0.5
          print(self.linear_proportion)
    
          # Create norm with vmin at 0 and midpoint at 0.5
          self.SymLogNorm1 = colors.SymLogNorm(lin_thres, lin_scale, vmin, 2*self.midpoint + np.abs(vmin))
          # Create norm with midpoint at 0.5 and vmax at 1
          self.SymLogNorm2 = colors.SymLogNorm(lin_thres, lin_scale, 2*self.midpoint - vmax, vmax)
          colors.SymLogNorm.__init__(self, lin_thres, lin_scale, vmin, vmax)
    
      def __get_value__(self, v, log_val1_i, log_val2_i, clip=None):
          v = np.array(v)
          x = [self.vmin, self.midpoint, self.vmax]
          y = [0., 0.5, 1.]
          interpol = np.interp(v, x, y)
          out = np.where(np.abs(v) < self.lin_thres, interpol, v)
          out = np.where(out > self.lin_thres, log_val2_i, out)
          out = np.where(out < self.lin_thres, log_val1_i, out)
          return np.ma.masked_array(out)
    
      def __call__(self, value, clip=None):
          log_val1 = self.SymLogNorm1(value)
          log_val2 = self.SymLogNorm2(value)
    
          out = [0] * len(value)
          for i, v in enumerate(value):
              out[i] = self.__get_value__(v, log_val1[i], log_val2[i])
          return np.ma.masked_array(out)