Skip to content

colorbar shows no ticks for decreasing norms #12665

@anntzer

Description

@anntzer

Bug report

Bug summary

When using a decreasing Norm, colorbars show no ticks.

Code for reproduction

This example uses a PowerNorm-like norm which first applies the power, then does linear rescaling to (0, 1) (as opposed to matplotlib's PowerNorm which first linearly rescales to (0, 1) then applies the power) -- as argued in #10234 the behavior here is often more desirable, though that's not the point of this bug report.

PNorms with a negative gamma are decreasing (the builtin PowerNorm wouldn't work here because the first linear rescaling step would put the minimum to zero, which doesn't like being raised to a negative power...).

from matplotlib import pyplot as plt
from matplotlib.colors import Normalize
import numpy as np


class PNorm(Normalize):
    def __init__(self, vmin=None, vmax=None, clip=False, *, gamma):
        super().__init__(vmin=vmin, vmax=vmax, clip=clip)
        self.gamma = gamma

    def __call__(self, value, clip=None):
        if clip is None:
            clip = self.clip
        result, is_scalar = self.process_value(value)
        self.autoscale_None(result)
        vmin, vmax = self.vmin, self.vmax
        gamma = self.gamma
        if vmin > vmax:
            raise ValueError("minvalue must be less than or equal to maxvalue")
        elif vmin == vmax:
            result.fill(0)
        else:
            if clip:
                mask = np.ma.getmask(result)
                result = np.ma.array(np.clip(result.filled(vmax), vmin, vmax),
                                     mask=mask)
            resdat = result.data
            resdat = resdat ** gamma
            resdat -= min(vmin ** gamma, vmax ** gamma)
            resdat /= abs(vmax ** gamma - vmin ** gamma)
            result = np.ma.array(resdat, mask=result.mask, copy=False)
        if is_scalar:
            result = result[0]
        return result

    def inverse(self, value):
        if not self.scaled():
            raise ValueError("Not invertible until scaled")
        if np.iterable(value):
            return np.vectorize(self.inverse)(value)
        else:
            vmin, vmax = self.vmin, self.vmax
            gamma = self.gamma
            return (value * abs(vmax ** gamma - vmin ** gamma)
                    + min(vmin ** gamma, vmax ** gamma)) ** (1 / gamma)


fig, axs = plt.subplots(1, 2)
im = axs[0].imshow(np.arange(1., 101.).reshape((10, 10)), norm=PNorm(gamma=2))
fig.colorbar(im, ax=axs[0])
im = axs[1].imshow(np.arange(1., 101.).reshape((10, 10)), norm=PNorm(gamma=-2))
fig.colorbar(im, ax=axs[1])

plt.show()

(as a side note, creating new norms is a bit ridiculously verbose)

Actual outcome

left is gamma=2, right is gamma=-2.

test

Expected outcome

Some ticks on the right (gamma=-2) colorbar.

Matplotlib version

  • Operating system: Arch Linux
  • Matplotlib version: 3.0.1
  • Matplotlib backend (print(matplotlib.get_backend())): any
  • Python version: 3.7

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions