Skip to content

Commit

Permalink
Add missing matplotlib colormaps
Browse files Browse the repository at this point in the history
closes #39
  • Loading branch information
has2k1 committed Mar 27, 2024
1 parent ecf27cc commit 5208199
Show file tree
Hide file tree
Showing 44 changed files with 1,963 additions and 349 deletions.
27 changes: 27 additions & 0 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,33 @@
Changelog
=========

v0.11.1
-------
*not-yet-released*

Bug Fixes
---------

- Fix :class:`mizani.palettes.brewer_pal` to return exact colors in the when
the requested colors are less than or equal to those in the palette.

- Add all matplotlib colormap and make them avalaible from
:class:`~mizani.palettes.cmap_pal` and
:class:`~mizani.palettes.cmap_d_pal` (:issue:`39`).

New
---

- Added :class:`~mizani.breaks.breaks_symlog` to calculate
breaks for the symmetric logarithm transformation.

Changes
-------
- The default `big_mark` for :class:`~mizani.labels.label_number`
has been changed from a comma to nothing.



v0.11.0
-------
*2024-02-12*
Expand Down
22 changes: 22 additions & 0 deletions mizani/_colors/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from ._colormaps._colormap import ColorMap, ColorMapKind
from ._colormaps._cubehelix import CubeHelixMap
from ._colormaps._interpolated import InterpolatedMap
from ._colormaps._listed import ListedMap
from ._colormaps._segment_function import SegmentFunctionMap
from ._colormaps._segment_interpolated import SegmentInterpolatedMap
from .hsluv import hex_to_rgb, rgb_to_hex
from .named_colors import get_colormap, get_named_color

__all__ = (
"ColorMap",
"ColorMapKind",
"CubeHelixMap",
"InterpolatedMap",
"ListedMap",
"SegmentFunctionMap",
"SegmentInterpolatedMap",
"get_colormap",
"get_named_color",
"hex_to_rgb",
"rgb_to_hex",
)
17 changes: 17 additions & 0 deletions mizani/_colors/_colormaps/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from ._colormap import ColorMap
from ._cubehelix import CubeHelixMap
from ._interpolated import InterpolatedMap
from ._listed import ListedMap
from ._palette_interpolated import PaletteInterpolatedMap
from ._segment_function import SegmentFunctionMap
from ._segment_interpolated import SegmentInterpolatedMap

__all__ = (
"ColorMap",
"CubeHelixMap",
"InterpolatedMap",
"ListedMap",
"PaletteInterpolatedMap",
"SegmentFunctionMap",
"SegmentInterpolatedMap",
)
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
from __future__ import annotations

import typing
import abc
from enum import Enum
from typing import TYPE_CHECKING

import numpy as np

if typing.TYPE_CHECKING:
if TYPE_CHECKING:
from typing import Sequence

from mizani.typing import (
FloatArrayLike,
RGBColor,
RGBColorArray,
RGBHexColor,
)
from mizani.typing import FloatArrayLike, RGBHexColor

__all__ = ("ColorMap",)


class ColorMap:
class ColorMapKind(Enum):
sequential = "sequential"
diverging = "diverging"
qualitative = "qualitative"
cyclic = "cyclic"
miscellaneous = "miscellaneous"


class ColorMap(abc.ABC):
"""
Base color for all color maps
"""

colors: Sequence[RGBHexColor] | Sequence[RGBColor] | RGBColorArray

@abc.abstractmethod
def _generate_colors(self, x: FloatArrayLike) -> Sequence[RGBHexColor]:
"""
Method to map [0, 1] values onto the a color range
Expand All @@ -36,7 +40,6 @@ def _generate_colors(self, x: FloatArrayLike) -> Sequence[RGBHexColor]:
Values in the range [0, 1]. O maps to the start of the
gradient, and 1 to the end of the gradient.
"""
return []

def discrete_palette(self, n: int) -> Sequence[RGBHexColor]:
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

import numpy as np

from ._colormap import ColorMap
from .hsluv import rgb_to_hex
from ..hsluv import rgb_to_hex
from ._colormap import ColorMap, ColorMapKind

if typing.TYPE_CHECKING:
from typing import Sequence
from typing import ClassVar, Sequence

from mizani.typing import (
FloatArrayLike,
Expand All @@ -33,6 +33,8 @@ class CubeHelixMap(ColorMap):
dark: float = 0.15
reverse: bool = False

kind: ClassVar[ColorMapKind] = ColorMapKind.miscellaneous

def _generate_colors(self, x: FloatArrayLike) -> Sequence[RGBHexColor]:
x = np.asarray(x)
# Apply gamma factor to emphasise low or high intensity values
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@

import numpy as np

from ._colormap import ColorMap
from .hsluv import hex_to_rgb, rgb_to_hex
from .named_colors import get_named_color
from ..hsluv import hex_to_rgb, rgb_to_hex
from ._colormap import ColorMap, ColorMapKind

if typing.TYPE_CHECKING:
from typing import Optional, Sequence
Expand All @@ -25,15 +24,39 @@
INNER_SPACE256 = SPACE256[1:-1]
ROUNDING_JITTER = 1e-12

__all__ = ("GradientMap",)

class _InterpolatedGen(ColorMap):
_r_lookup: NDArrayFloat
_g_lookup: NDArrayFloat
_b_lookup: NDArrayFloat

def _generate_colors(self, x: FloatArrayLike) -> Sequence[RGBHexColor]:
"""
Lookup colors in the interpolated ranges
Parameters
----------
x :
Values in the range [0, 1]. O maps to the start of the
gradient, and 1 to the end of the gradient.
"""
x = np.asarray(x)
idx = np.round((x * 255) + ROUNDING_JITTER).astype(int)
arr = np.column_stack(
[self._r_lookup[idx], self._g_lookup[idx], self._b_lookup[idx]],
)
return [rgb_to_hex(c) for c in arr]


@dataclass
class GradientMap(ColorMap):
class InterpolatedMap(_InterpolatedGen):
colors: Sequence[RGBHexColor] | Sequence[RGBColor] | RGBColorArray
values: Optional[Sequence[float]] = None
kind: ColorMapKind = ColorMapKind.miscellaneous

def __post_init__(self):
from ..named_colors import get_named_color

if self.values is None:
values = np.linspace(0, 1, len(self.colors))
elif len(self.colors) < 2:
Expand Down Expand Up @@ -66,25 +89,12 @@ def __post_init__(self):
self._g_lookup = interp_lookup(values, self._data[:, 1])
self._b_lookup = interp_lookup(values, self._data[:, 2])

def _generate_colors(self, x: FloatArrayLike) -> Sequence[RGBHexColor]:
"""
Lookup colors in the interpolated ranges

Parameters
----------
x :
Values in the range [0, 1]. O maps to the start of the
gradient, and 1 to the end of the gradient.
"""
x = np.asarray(x)
idx = np.round((x * 255) + ROUNDING_JITTER).astype(int)
arr = np.column_stack(
[self._r_lookup[idx], self._g_lookup[idx], self._b_lookup[idx]]
)
return [rgb_to_hex(c) for c in arr]


def interp_lookup(x: NDArrayFloat, values: NDArrayFloat) -> NDArrayFloat:
def interp_lookup(
x: NDArrayFloat,
values: NDArrayFloat,
values_alt: Optional[NDArrayFloat] = None,
) -> NDArrayFloat:
"""
Create an interpolation lookup array
Expand All @@ -98,7 +108,25 @@ def interp_lookup(x: NDArrayFloat, values: NDArrayFloat) -> NDArrayFloat:
should be sorted.
values:
In the range [0, 1]. Must be the same length as x.
values_alt:
In the range [0, 1]. Must be the same length as x.
Makes it possible to have adjacent interpolation regions
that with gaps in them numbers. e.g.
values = [0, 0.1, 0.5, 1]
values_alt = [0, 0.1, 0.6, 1]
Creates the regions
[(0, 0.1), (0.1, 0.5), (0.6, 1)]
If values_alt is None the region would be
[(0, 0.1), (0.1, 0.5), (0.5, 1)]
"""
if values_alt is None:
values_alt = values

# - Map x from [0, 1] onto [0, 255] i.e. the color channel
# breaks (continuous)
# - Find where x would be mapped onto the grid (discretizing)
Expand All @@ -111,10 +139,12 @@ def interp_lookup(x: NDArrayFloat, values: NDArrayFloat) -> NDArrayFloat:
ind = np.searchsorted(x256, SPACE256)[1:-1]
ind_prev = ind - 1
distance = (INNER_SPACE256 - x256[ind_prev]) / (x256[ind] - x256[ind_prev])
stop = values[ind]
start = values_alt[ind_prev]
lut = np.concatenate(
[
[values[0]],
distance * (values[ind] - values[ind_prev]) + values[ind_prev],
start + distance * (stop - start),
[values[-1]],
]
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@

import numpy as np

from ._colormap import ColorMap
from .hsluv import hex_to_rgb, rgb_to_hex
from .named_colors import get_named_color
from ..hsluv import hex_to_rgb, rgb_to_hex
from ._colormap import ColorMap, ColorMapKind

if typing.TYPE_CHECKING:
from typing import Sequence
Expand All @@ -30,8 +29,11 @@
@dataclass
class ListedMap(ColorMap):
colors: Sequence[RGBHexColor] | Sequence[RGBColor] | RGBColorArray
kind: ColorMapKind = ColorMapKind.miscellaneous

def __post_init__(self):
from ..named_colors import get_named_color

self.values = np.linspace(0, 1, len(self.colors))
if isinstance(self.colors[0], str):
colors = [
Expand Down
Empty file.
59 changes: 59 additions & 0 deletions mizani/_colors/_colormaps/_maps/_interpolated.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from mizani._colors import ColorMapKind as cmk
from mizani._colors import InterpolatedMap

__all__ = (
# diverging
"bwr",
"seismic",
# miscellaneous
"gist_rainbow",
"terrain",
)


seismic = InterpolatedMap(
colors=(
(0.0, 0.0, 0.3),
(0.0, 0.0, 1.0),
(1.0, 1.0, 1.0),
(1.0, 0.0, 0.0),
(0.5, 0.0, 0.0),
),
kind=cmk.diverging,
)

bwr = InterpolatedMap(
colors=(
(0.0, 0.0, 1.0),
(1.0, 1.0, 1.0),
(1.0, 0.0, 0.0),
),
kind=cmk.diverging,
)


gist_rainbow = InterpolatedMap(
colors=(
(1.00, 0.00, 0.16),
(1.00, 0.00, 0.00),
(1.00, 1.00, 0.00),
(0.00, 1.00, 0.00),
(0.00, 1.00, 1.00),
(0.00, 0.00, 1.00),
(1.00, 0.00, 1.00),
(1.00, 0.00, 0.75),
),
values=(0.000, 0.030, 0.215, 0.400, 0.586, 0.770, 0.954, 1.000),
)

terrain = InterpolatedMap(
colors=(
(0.2, 0.2, 0.6),
(0.0, 0.6, 1.0),
(0.0, 0.8, 0.4),
(1.0, 1.0, 0.6),
(0.5, 0.36, 0.33),
(1.0, 1.0, 1.0),
),
values=(0.00, 0.15, 0.25, 0.50, 0.75, 1.00),
)
Loading

0 comments on commit 5208199

Please sign in to comment.