Skip to content

Commit 5208199

Browse files
committed
Add missing matplotlib colormaps
closes #39
1 parent ecf27cc commit 5208199

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1963
-349
lines changed

doc/changelog.rst

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,33 @@
11
Changelog
22
=========
33

4+
v0.11.1
5+
-------
6+
*not-yet-released*
7+
8+
Bug Fixes
9+
---------
10+
11+
- Fix :class:`mizani.palettes.brewer_pal` to return exact colors in the when
12+
the requested colors are less than or equal to those in the palette.
13+
14+
- Add all matplotlib colormap and make them avalaible from
15+
:class:`~mizani.palettes.cmap_pal` and
16+
:class:`~mizani.palettes.cmap_d_pal` (:issue:`39`).
17+
18+
New
19+
---
20+
21+
- Added :class:`~mizani.breaks.breaks_symlog` to calculate
22+
breaks for the symmetric logarithm transformation.
23+
24+
Changes
25+
-------
26+
- The default `big_mark` for :class:`~mizani.labels.label_number`
27+
has been changed from a comma to nothing.
28+
29+
30+
431
v0.11.0
532
-------
633
*2024-02-12*

mizani/_colors/__init__.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from ._colormaps._colormap import ColorMap, ColorMapKind
2+
from ._colormaps._cubehelix import CubeHelixMap
3+
from ._colormaps._interpolated import InterpolatedMap
4+
from ._colormaps._listed import ListedMap
5+
from ._colormaps._segment_function import SegmentFunctionMap
6+
from ._colormaps._segment_interpolated import SegmentInterpolatedMap
7+
from .hsluv import hex_to_rgb, rgb_to_hex
8+
from .named_colors import get_colormap, get_named_color
9+
10+
__all__ = (
11+
"ColorMap",
12+
"ColorMapKind",
13+
"CubeHelixMap",
14+
"InterpolatedMap",
15+
"ListedMap",
16+
"SegmentFunctionMap",
17+
"SegmentInterpolatedMap",
18+
"get_colormap",
19+
"get_named_color",
20+
"hex_to_rgb",
21+
"rgb_to_hex",
22+
)

mizani/_colors/_colormaps/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from ._colormap import ColorMap
2+
from ._cubehelix import CubeHelixMap
3+
from ._interpolated import InterpolatedMap
4+
from ._listed import ListedMap
5+
from ._palette_interpolated import PaletteInterpolatedMap
6+
from ._segment_function import SegmentFunctionMap
7+
from ._segment_interpolated import SegmentInterpolatedMap
8+
9+
__all__ = (
10+
"ColorMap",
11+
"CubeHelixMap",
12+
"InterpolatedMap",
13+
"ListedMap",
14+
"PaletteInterpolatedMap",
15+
"SegmentFunctionMap",
16+
"SegmentInterpolatedMap",
17+
)

mizani/colors/_colormap.py renamed to mizani/_colors/_colormaps/_colormap.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,33 @@
11
from __future__ import annotations
22

3-
import typing
3+
import abc
4+
from enum import Enum
5+
from typing import TYPE_CHECKING
46

57
import numpy as np
68

7-
if typing.TYPE_CHECKING:
9+
if TYPE_CHECKING:
810
from typing import Sequence
911

10-
from mizani.typing import (
11-
FloatArrayLike,
12-
RGBColor,
13-
RGBColorArray,
14-
RGBHexColor,
15-
)
12+
from mizani.typing import FloatArrayLike, RGBHexColor
1613

1714
__all__ = ("ColorMap",)
1815

1916

20-
class ColorMap:
17+
class ColorMapKind(Enum):
18+
sequential = "sequential"
19+
diverging = "diverging"
20+
qualitative = "qualitative"
21+
cyclic = "cyclic"
22+
miscellaneous = "miscellaneous"
23+
24+
25+
class ColorMap(abc.ABC):
2126
"""
2227
Base color for all color maps
2328
"""
2429

25-
colors: Sequence[RGBHexColor] | Sequence[RGBColor] | RGBColorArray
26-
30+
@abc.abstractmethod
2731
def _generate_colors(self, x: FloatArrayLike) -> Sequence[RGBHexColor]:
2832
"""
2933
Method to map [0, 1] values onto the a color range
@@ -36,7 +40,6 @@ def _generate_colors(self, x: FloatArrayLike) -> Sequence[RGBHexColor]:
3640
Values in the range [0, 1]. O maps to the start of the
3741
gradient, and 1 to the end of the gradient.
3842
"""
39-
return []
4043

4144
def discrete_palette(self, n: int) -> Sequence[RGBHexColor]:
4245
"""

mizani/colors/_cubehelix.py renamed to mizani/_colors/_colormaps/_cubehelix.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55

66
import numpy as np
77

8-
from ._colormap import ColorMap
9-
from .hsluv import rgb_to_hex
8+
from ..hsluv import rgb_to_hex
9+
from ._colormap import ColorMap, ColorMapKind
1010

1111
if typing.TYPE_CHECKING:
12-
from typing import Sequence
12+
from typing import ClassVar, Sequence
1313

1414
from mizani.typing import (
1515
FloatArrayLike,
@@ -33,6 +33,8 @@ class CubeHelixMap(ColorMap):
3333
dark: float = 0.15
3434
reverse: bool = False
3535

36+
kind: ClassVar[ColorMapKind] = ColorMapKind.miscellaneous
37+
3638
def _generate_colors(self, x: FloatArrayLike) -> Sequence[RGBHexColor]:
3739
x = np.asarray(x)
3840
# Apply gamma factor to emphasise low or high intensity values

mizani/colors/_gradient.py renamed to mizani/_colors/_colormaps/_interpolated.py

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55

66
import numpy as np
77

8-
from ._colormap import ColorMap
9-
from .hsluv import hex_to_rgb, rgb_to_hex
10-
from .named_colors import get_named_color
8+
from ..hsluv import hex_to_rgb, rgb_to_hex
9+
from ._colormap import ColorMap, ColorMapKind
1110

1211
if typing.TYPE_CHECKING:
1312
from typing import Optional, Sequence
@@ -25,15 +24,39 @@
2524
INNER_SPACE256 = SPACE256[1:-1]
2625
ROUNDING_JITTER = 1e-12
2726

28-
__all__ = ("GradientMap",)
27+
28+
class _InterpolatedGen(ColorMap):
29+
_r_lookup: NDArrayFloat
30+
_g_lookup: NDArrayFloat
31+
_b_lookup: NDArrayFloat
32+
33+
def _generate_colors(self, x: FloatArrayLike) -> Sequence[RGBHexColor]:
34+
"""
35+
Lookup colors in the interpolated ranges
36+
37+
Parameters
38+
----------
39+
x :
40+
Values in the range [0, 1]. O maps to the start of the
41+
gradient, and 1 to the end of the gradient.
42+
"""
43+
x = np.asarray(x)
44+
idx = np.round((x * 255) + ROUNDING_JITTER).astype(int)
45+
arr = np.column_stack(
46+
[self._r_lookup[idx], self._g_lookup[idx], self._b_lookup[idx]],
47+
)
48+
return [rgb_to_hex(c) for c in arr]
2949

3050

3151
@dataclass
32-
class GradientMap(ColorMap):
52+
class InterpolatedMap(_InterpolatedGen):
3353
colors: Sequence[RGBHexColor] | Sequence[RGBColor] | RGBColorArray
3454
values: Optional[Sequence[float]] = None
55+
kind: ColorMapKind = ColorMapKind.miscellaneous
3556

3657
def __post_init__(self):
58+
from ..named_colors import get_named_color
59+
3760
if self.values is None:
3861
values = np.linspace(0, 1, len(self.colors))
3962
elif len(self.colors) < 2:
@@ -66,25 +89,12 @@ def __post_init__(self):
6689
self._g_lookup = interp_lookup(values, self._data[:, 1])
6790
self._b_lookup = interp_lookup(values, self._data[:, 2])
6891

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

73-
Parameters
74-
----------
75-
x :
76-
Values in the range [0, 1]. O maps to the start of the
77-
gradient, and 1 to the end of the gradient.
78-
"""
79-
x = np.asarray(x)
80-
idx = np.round((x * 255) + ROUNDING_JITTER).astype(int)
81-
arr = np.column_stack(
82-
[self._r_lookup[idx], self._g_lookup[idx], self._b_lookup[idx]]
83-
)
84-
return [rgb_to_hex(c) for c in arr]
85-
86-
87-
def interp_lookup(x: NDArrayFloat, values: NDArrayFloat) -> NDArrayFloat:
93+
def interp_lookup(
94+
x: NDArrayFloat,
95+
values: NDArrayFloat,
96+
values_alt: Optional[NDArrayFloat] = None,
97+
) -> NDArrayFloat:
8898
"""
8999
Create an interpolation lookup array
90100
@@ -98,7 +108,25 @@ def interp_lookup(x: NDArrayFloat, values: NDArrayFloat) -> NDArrayFloat:
98108
should be sorted.
99109
values:
100110
In the range [0, 1]. Must be the same length as x.
111+
values_alt:
112+
In the range [0, 1]. Must be the same length as x.
113+
Makes it possible to have adjacent interpolation regions
114+
that with gaps in them numbers. e.g.
115+
116+
values = [0, 0.1, 0.5, 1]
117+
values_alt = [0, 0.1, 0.6, 1]
118+
119+
Creates the regions
120+
121+
[(0, 0.1), (0.1, 0.5), (0.6, 1)]
122+
123+
If values_alt is None the region would be
124+
125+
[(0, 0.1), (0.1, 0.5), (0.5, 1)]
101126
"""
127+
if values_alt is None:
128+
values_alt = values
129+
102130
# - Map x from [0, 1] onto [0, 255] i.e. the color channel
103131
# breaks (continuous)
104132
# - Find where x would be mapped onto the grid (discretizing)
@@ -111,10 +139,12 @@ def interp_lookup(x: NDArrayFloat, values: NDArrayFloat) -> NDArrayFloat:
111139
ind = np.searchsorted(x256, SPACE256)[1:-1]
112140
ind_prev = ind - 1
113141
distance = (INNER_SPACE256 - x256[ind_prev]) / (x256[ind] - x256[ind_prev])
142+
stop = values[ind]
143+
start = values_alt[ind_prev]
114144
lut = np.concatenate(
115145
[
116146
[values[0]],
117-
distance * (values[ind] - values[ind_prev]) + values[ind_prev],
147+
start + distance * (stop - start),
118148
[values[-1]],
119149
]
120150
)

mizani/colors/_listed.py renamed to mizani/_colors/_colormaps/_listed.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55

66
import numpy as np
77

8-
from ._colormap import ColorMap
9-
from .hsluv import hex_to_rgb, rgb_to_hex
10-
from .named_colors import get_named_color
8+
from ..hsluv import hex_to_rgb, rgb_to_hex
9+
from ._colormap import ColorMap, ColorMapKind
1110

1211
if typing.TYPE_CHECKING:
1312
from typing import Sequence
@@ -30,8 +29,11 @@
3029
@dataclass
3130
class ListedMap(ColorMap):
3231
colors: Sequence[RGBHexColor] | Sequence[RGBColor] | RGBColorArray
32+
kind: ColorMapKind = ColorMapKind.miscellaneous
3333

3434
def __post_init__(self):
35+
from ..named_colors import get_named_color
36+
3537
self.values = np.linspace(0, 1, len(self.colors))
3638
if isinstance(self.colors[0], str):
3739
colors = [

mizani/_colors/_colormaps/_maps/__init__.py

Whitespace-only changes.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from mizani._colors import ColorMapKind as cmk
2+
from mizani._colors import InterpolatedMap
3+
4+
__all__ = (
5+
# diverging
6+
"bwr",
7+
"seismic",
8+
# miscellaneous
9+
"gist_rainbow",
10+
"terrain",
11+
)
12+
13+
14+
seismic = InterpolatedMap(
15+
colors=(
16+
(0.0, 0.0, 0.3),
17+
(0.0, 0.0, 1.0),
18+
(1.0, 1.0, 1.0),
19+
(1.0, 0.0, 0.0),
20+
(0.5, 0.0, 0.0),
21+
),
22+
kind=cmk.diverging,
23+
)
24+
25+
bwr = InterpolatedMap(
26+
colors=(
27+
(0.0, 0.0, 1.0),
28+
(1.0, 1.0, 1.0),
29+
(1.0, 0.0, 0.0),
30+
),
31+
kind=cmk.diverging,
32+
)
33+
34+
35+
gist_rainbow = InterpolatedMap(
36+
colors=(
37+
(1.00, 0.00, 0.16),
38+
(1.00, 0.00, 0.00),
39+
(1.00, 1.00, 0.00),
40+
(0.00, 1.00, 0.00),
41+
(0.00, 1.00, 1.00),
42+
(0.00, 0.00, 1.00),
43+
(1.00, 0.00, 1.00),
44+
(1.00, 0.00, 0.75),
45+
),
46+
values=(0.000, 0.030, 0.215, 0.400, 0.586, 0.770, 0.954, 1.000),
47+
)
48+
49+
terrain = InterpolatedMap(
50+
colors=(
51+
(0.2, 0.2, 0.6),
52+
(0.0, 0.6, 1.0),
53+
(0.0, 0.8, 0.4),
54+
(1.0, 1.0, 0.6),
55+
(0.5, 0.36, 0.33),
56+
(1.0, 1.0, 1.0),
57+
),
58+
values=(0.00, 0.15, 0.25, 0.50, 0.75, 1.00),
59+
)

0 commit comments

Comments
 (0)