Makes PowerNorm consistent with other norms by Sreekanth-M8 · Pull Request #30862 · matplotlib/matplotlib · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 33 additions & 55 deletions lib/matplotlib/colors.py
11 changes: 8 additions & 3 deletions lib/matplotlib/colors.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -339,14 +339,16 @@ def make_norm_from_scale(
scale_cls: type[scale.ScaleBase],
base_norm_cls: type[Normalize],
*,
init: Callable | None = ...
init: Callable | None = ...,
norm_before_trf: bool = ...,
) -> type[Normalize]: ...
@overload
def make_norm_from_scale(
scale_cls: type[scale.ScaleBase],
base_norm_cls: None = ...,
*,
init: Callable | None = ...
init: Callable | None = ...,
norm_before_trf: bool = ...,
) -> Callable[[type[Normalize]], type[Normalize]]: ...

class FuncNorm(Normalize):
Expand Down Expand Up @@ -389,14 +391,17 @@ class AsinhNorm(Normalize):
def linear_width(self, value: float) -> None: ...

class PowerNorm(Normalize):
gamma: float
def __init__(
self,
gamma: float,
vmin: float | None = ...,
vmax: float | None = ...,
clip: bool = ...,
) -> None: ...
@property
def gamma(self) -> float: ...
@gamma.setter
def gamma(self, value: float) -> None: ...

class BoundaryNorm(Normalize):
boundaries: np.ndarray
Expand Down
106 changes: 106 additions & 0 deletions lib/matplotlib/scale.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"log" `LogScale` `LogTransform` `InvertedLogTransform`
"logit" `LogitScale` `LogitTransform` `LogisticTransform`
"symlog" `SymmetricalLogScale` `SymmetricalLogTransform` `InvertedSymmetricalLogTransform`
"power" `PowerScale` `PowerTransform` `InvertedPowerTransform`
============= ===================== ================================ =================================

A user will often only use the scale name, e.g. when setting the scale through
Expand Down Expand Up @@ -282,6 +283,109 @@ def set_default_locators_and_formatters(self, axis):
axis.set_minor_locator(NullLocator())


class PowerTransform(Transform):
"""
A simple power transformation used by `.PowerScale`.

This transformation applies a power-law scaling to positive values, while
nonpositive values remain unchanged.
"""
input_dims = output_dims = 1

def __init__(self, gamma):
"""
Parameters
----------
gamma : float
Power law exponent.
"""
super().__init__()
self.gamma = gamma

def __str__(self):
return "{}(gamma={})".format(
type(self).__name__, self.gamma)

def transform_non_affine(self, values):
with np.errstate(divide="ignore", invalid="ignore"):
nonpos = ~(values > 0)
out = np.power(values, self.gamma)
out[nonpos] = values[nonpos]
return out

def inverted(self):
return InvertedPowerTransform(self.gamma)


class InvertedPowerTransform(Transform):
"""
The inverse of the `.PowerTransform`.

This transformation applies an inverse power-law scaling to positive values,
while nonpositive values remain unchanged.
"""
input_dims = output_dims = 1

def __init__(self, gamma):
"""
Parameters
----------
gamma : float
Power law exponent.
"""
super().__init__()
if gamma == 0:
raise ValueError('gamma cannot be 0')
self.gamma = gamma

def transform_non_affine(self, values):
with np.errstate(divide="ignore", invalid="ignore"):
nonpos = ~(values > 0)
out = np.power(values, 1.0 / self.gamma)
out[nonpos] = values[nonpos]
return out

def inverted(self):
return PowerTransform(self.gamma)


class PowerScale(ScaleBase):
"""
A standard power scale
"""
name = 'power'

@_make_axis_parameter_optional
def __init__(self, axis=None, *, gamma=0.5):
"""
Parameters
----------
axis : `~matplotlib.axis.Axis`
The axis for the scale.
gamma : float, default: 0.5
Power law exponent.
"""
self._transform = PowerTransform(gamma)

gamma = property(lambda self: self._transform.gamma)

def get_transform(self):
"""Return the `.PowerTransform` associated with this scale."""
return self._transform

def set_default_locators_and_formatters(self, axis):
# docstring inherited
axis.set_major_locator(AutoLocator())
axis.set_major_formatter(ScalarFormatter())
axis.set_minor_formatter(NullFormatter())
# update the minor locator for x and y axis based on rcParams
if (axis.axis_name == 'x' and mpl.rcParams['xtick.minor.visible'] or
axis.axis_name == 'y' and mpl.rcParams['ytick.minor.visible']):
axis.set_minor_locator(AutoMinorLocator())
else:
axis.set_minor_locator(NullLocator())


class LogTransform(Transform):
input_dims = output_dims = 1

Expand Down Expand Up @@ -807,6 +911,7 @@ def limit_range_for_scale(self, vmin, vmax, minpos):
'logit': LogitScale,
'function': FuncScale,
'functionlog': FuncScaleLog,
'power': PowerScale,
}

# caching of signature info
Expand All @@ -821,6 +926,7 @@ def limit_range_for_scale(self, vmin, vmax, minpos):
'logit': True,
'function': True,
'functionlog': True,
'power': True,
}


Expand Down
23 changes: 23 additions & 0 deletions lib/matplotlib/scale.pyi
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading