Source code for moptipy.utils.plot_defaults

"""Default styles for plots."""
from typing import Final, cast

import matplotlib.cm as mplcm  # type: ignore
import matplotlib.pyplot as plt  # type: ignore
import numpy as np
from matplotlib import colors  # type: ignore
from pycommons.types import check_int_range, type_error

#: The internal color black.
COLOR_BLACK: Final[tuple[float, float, float]] = (0.0, 0.0, 0.0)
#: The internal color white.
COLOR_WHITE: Final[tuple[float, float, float]] = (1.0, 1.0, 1.0)


[docs] def str_to_palette(palette: str) \ -> tuple[tuple[float, float, float], ...]: """ Obtain a palette from a string. :param palette: the string with all the color data. :returns: the palette """ if isinstance(colors, str): raise type_error(colors, "colors", str) result: list[tuple[float, float, float]] = [] end: int = -1 length: Final[int] = len(palette) while end < length: start: int = end + 1 end = length for ch in "\n\r\t ,;": end_new: int = palette.find(ch, start) if start < end_new < end: end = end_new color: str = palette[start:end].strip() if color.startswith("#"): color = color[1:].strip() if len(color) > 0: if len(color) != 6: raise ValueError(f"invalid color: {color!r} in {palette!r}") result.append((int(color[0:2], 16) / 255, int(color[2:4], 16) / 255, int(color[4:6], 16) / 255)) if len(result) <= 0: raise ValueError(f"empty colors: {palette!r}") return tuple(result)
#: A palette with 11 distinct colors. __PALETTE_11: Final[tuple[tuple[float, float, float], ...]] = str_to_palette( "ff0000 0000ff 00d900 a32626 ffa500 008833 7788ff cf00ff 770066 cccc00 " "a0a9a0") #: A palette with 21 distinct colors. __PALETTE_21: Final[tuple[tuple[float, float, float], ...]] = str_to_palette( "e6194b 3cb44b ffe119 0082c8 f58231 911eb4 46f0f0 f032e6 d2f53c fabebe " "008080 e6beff aa6e28 cfca08 800000 aaffc3 808000 ffd8b1 000080 505000 " "90a0a0") #: A palette with 25 distinct colors. __PALETTE_25: Final[tuple[tuple[float, float, float], ...]] = str_to_palette( "20b2aa f0e68c b8860b ff00ff 00fa9a ee82ee 9acd32 dcdcdc 556b2f db7093 " "00bfff 1e90ff 8a2be2 ff1493 008000 ffd700 000080 800000 7fff00 8b008b " "0000ff 483d8b ff0000 696969 ff7f50") #: A palette with 28 distinct colors. __PALETTE_28: Final[tuple[tuple[float, float, float], ...]] = str_to_palette( "545454 550000 b0b0ff bababa 878787 005500 550055 b000b0 e4e400 00b0b0 " "00ffff 00ff00 878700 870087 005555 ff0000 0000ff baba00 00b000 ff00ff " "870000 4949ff 008700 8484ff 008787 e4e4e4 545400 b00000") #: A palette with 30 distinct colors. __PALETTE_30: Final[tuple[tuple[float, float, float], ...]] = str_to_palette( "00bfff ff1493 7f007f 8a2be2 1e90ff adff2f ffb6c1 fa8072 ffff54 90ee90 " "ff4500 00ff00 483d8b ffa500 696969 add8e6 ff00ff 7f0000 8fbc8f da70d6 " "006400 dc143c 00ff7f 008b8b 808000 b03060 00ffff 0000ff 000080 deb887 ") #: A palette with 35 distinct colors. __PALETTE_35: Final[tuple[tuple[float, float, float], ...]] = str_to_palette( "228b22 808080 9acd32 008080 483d8b 7f0000 d2b48c 00ff00 00008b 808000 " "00ced1 8fbc8f 556b2f ffc0cb 1e90ff ffff54 800080 00bfff ff00ff 7b68ee " "ff6347 9400d3 ffa500 7fffd4 b03060 ff0000 f08080 f4a460 00ff7f 98fb98 " "f0e68c ff1493 0000ff b0e0e6 dda0dd") #: A palette with 40 distinct colors. __PALETTE_40: Final[tuple[tuple[float, float, float], ...]] = str_to_palette( "ff00ff 7f007f a9a9a9 0000ff daa520 2f4f4f da70d6 ff8c00 ffc0cb afeeee " "00008b 20b2aa 00ff00 f4a460 8b0000 008000 f08080 a0522d ff6347 808000 " "bdb76b 98fb98 191970 ffe4c4 ffff00 4682b4 32cd32 1e90ff 3cb371 ff1493 " "b03060 7fffd4 556b2f dda0dd adff2f 7b68ee 9acd32 ff0000 00bfff 9400d3 ") #: A palette with 45 distinct colors. __PALETTE_45: Final[tuple[tuple[float, float, float], ...]] = str_to_palette( "ffb60c 00008b 8b4513 e9967a a020f0 ff0000 6a5acd 808080 008b8b 00ff00 " "90ee90 4682b4 daa520 7f007f ffd700 00bfff 00ced1 556b2f c0c0c0 008000 " "ff69b4 dda0dd adff2f 3cb371 8b0000 0000ff f5deb3 32cd32 1e90ff b03060 " "ff7f50 cd5c5c ff00ff 7fffd4 6b8e23 dc143c 9acd32 afeeee 2f4f4f 00fa9a " "191970 8fbc8f ba55d3 ff8c00 bdb76b") #: A palette with 50 distinct colors. __PALETTE_50: Final[tuple[tuple[float, float, float], ...]] = str_to_palette( "7fffd4 000080 00ff7f 483d8b 008000 afeeee ffc0cb daa520 ffd700 ff8c00 " "87cefa ff00ff c0c0c0 cd5c5c 00ff00 adff2f e9967a ffff00 dc143c cd853f " "9932cc 98fb98 708090 800000 ee82ee 800080 8fbc8f 4682b4 dda0dd 32cd32 " "a020f0 48d1cc a0522d ff1493 ff69b4 f0e68c 9370db 2f4f4f ff0000 6b8e23 " "556b2f ff6347 f5deb3 008b8b db7093 0000cd 9acd32 3cb371 0000ff 6495ed") #: A palette with 60 distinct colors. __PALETTE_60: Final[tuple[tuple[float, float, float], ...]] = str_to_palette( "d2b48c 191970 000080 20b2aa bc8f8f 2f4f4f 8b4513 ff4500 dc143c afeeee " "db7093 c71585 bdb76b b03060 708090 00ffff 6495ed 808000 f08080 b8860b " "cd853f 556b2f ffa07a 008080 eee8aa dda0dd ee82ee 7fffd4 9370db 7f007f " "d8bfd8 9acd32 4682b4 98fb98 00ff00 9932cc 0000cd d2691e 00fa9a ff8c00 " "663399 a9a9a9 ffff00 32cd32 8b0000 4169e1 8fbc8f 0000ff ff69b4 008000 " "ffb6c1 00bfff ffd700 ff1493 ff00ff 3cb371 a020f0 87ceeb ff6347 adff2f") #: A palette with 70 distinct colors. __PALETTE_70: Final[tuple[tuple[float, float, float], ...]] = str_to_palette( "b8860b a52a2a ffe4c4 663399 da70d6 a020f0 eee8aa 9acd32 ba55d3 ff8c00 " "00008b 2e8b57 66cdaa 2f4f4f 0000cd 32cd32 556b2f adff2f 7fffd4 00ff00 " "008b8b 4b0082 ffd700 00bfff 6b8e23 48d1cc b03060 778899 ffff54 ff0000 " "00ff7f d2b48c 3cb371 bc8f8f b0e0e6 ffc0cb dcdcdc ff00ff cd5c5c 6495ed " "0000ff 9370db dc143c a9a9a9 bdb76b f4a460 c71585 e9967a 4682b4 db7093 " "483d8b 8b008b 87cefa 7fff00 dda0dd 006400 9932cc 808000 ff1493 191970 " "4169e1 ff6347 b0c4de ff69b4 d2691e 00ffff 98fb98 ffff00 a0522d 8fbc8f") #: A palette with 80 distinct colors. __PALETTE_80: Final[tuple[tuple[float, float, float], ...]] = str_to_palette( "eee8aa bdb76b 2e8b57 0000ff 1e90ff cd5c5c ffd700 90ee90 00ffff ba55d3 " "d3d3d3 40e0d0 ff0000 b0c4de 008000 0000cd 00bfff ffa07a ff00ff 778899 " "dc143c cd853f 556b2f 9acd32 9400d3 663399 b03060 ffdab9 b22222 ffa500 " "7fffd4 4169e1 dda0dd 8fbc8f ffff00 ff1493 191970 00008b 9932cc 006400 " "483d8b 808000 696969 00ff7f d8bfd8 d2691e 4682b4 a9a9a9 c71585 5f9ea0 " "ffc0cb 7f0000 800080 ee82ee 008080 4b0082 ffff54 9370db 8b4513 ff6347 " "daa520 87cefa adff2f ff8c00 6b8e23 deb887 00fa9a 66cdaa 7b68ee fa8072 " "ff69b4 db7093 b0e0e6 00ff00 2f4f4f bc8f8f 32cd32 3cb371 20b2aa 7cfc00") #: A palette with 90 distinct colors. __PALETTE_90: Final[tuple[tuple[float, float, float], ...]] = str_to_palette( "fa8072 ffff54 0000cd 32cd32 dc143c 40e0d0 ff8c00 9932cc 7b68ee 800000 " "4b0082 008000 afeeee ee82ee ffd700 6a5acd 2e8b57 eee8aa a9a9a9 ff69b4 " "b8860b c71585 ff4500 7fff00 2f4f4f 00ff7f ffa500 add8e6 008b8b bdb76b " "ff00ff cd853f f0e68c 808080 db7093 800080 a0522d 87ceeb 3cb371 9400d3 " "b0c4de daa520 b03060 dda0dd 66cdaa 6b8e23 d3d3d3 4682b4 9acd32 4169e1 " "ff7f50 5f9ea0 f08080 00fa9a 00ced1 808000 adff2f 0000ff d2691e ff1493 " "ff6347 ffdead 20b2aa 006400 7fffd4 483d8b cd5c5c f4a460 8fbc8f 663399 " "d8bfd8 9370db b22222 d2b48c ffff00 ba55d3 bc8f8f ffb6c1 6495ed e9967a " "ff0000 000080 00ff00 191970 90ee90 00ffff 778899 1e90ff 00bfff 556b2f") #: A palette with 100 distinct colors. __PALETTE_100: Final[tuple[tuple[float, float, float], ...]] = str_to_palette( "bdb76b 8a2be2 bc8f8f eee8aa 8fbc8f a020f0 ff4500 2f4f4f 000080 ff6347 " "6b8e23 808080 663399 afeeee dcdcdc ff8c00 3cb371 191970 4682b4 7cfc00 " "ee82ee 32cd32 9370db ff1493 00ff00 6a5acd adff2f 006400 00fa9a ffd700 " "d2691e c0c0c0 00ff7f ba55d3 add8e6 ffff00 6495ed 8b008b 48d1cc 87cefa " "fa8072 deb887 b22222 7b68ee b8860b 90ee90 8b4513 9acd32 cd853f f08080 " "ff00ff da70d6 9400d3 ff69b4 ffe4b5 556b2f 8b0000 1e90ff e9967a 696969 " "c71585 ffa07a f0e68c a9a9a9 20b2aa a0522d 87ceeb 0000ff 008000 daa520 " "00ffff b03060 483d8b ffb6c1 dda0dd ff0000 9932cc 708090 cd5c5c 4b0082 " "ffa500 0000cd dc143c a52a2a 008080 7fffd4 5f9ea0 b0c4de d8bfd8 808000 " "db7093 f4a460 2e8b57 ffff54 40e0d0 66cdaa ffe4c4 ff7f50 4169e1 00bfff") #: A palette with 110 distinct colors. __PALETTE_110: Final[tuple[tuple[float, float, float], ...]] = str_to_palette( "0000ff 556b2f ff4500 000080 f5deb3 ff1493 00008b 0000cd ff8c00 ffc0cb " "fa8072 696969 da70d6 8b4513 d2691e 00fa9a 008080 c71585 bdb76b 40e0d0 " "6495ed daa520 5f9ea0 ffe4c4 f4a460 ba55d3 228b22 00ced1 00ffff 66cdaa " "b03060 9400d3 f08080 dc143c adff2f 00ff00 9932cc ee82ee 8a2be2 32cd32 " "afeeee 191970 808080 9acd32 90ee90 b22222 a0522d ff00ff 006400 c0c0c0 " "2f4f4f d3d3d3 cd853f 98fb98 9370db 4169e1 4b0082 00ff7f ff7f50 8fbc8f " "8b0000 eee8aa dda0dd 008000 ffa500 ff6347 ff69b4 d8bfd8 ffa07a 4682b4 " "b0c4de 2e8b57 ff0000 00bfff f0e68c 7f0000 b8860b d2b48c ffff54 1e90ff " "e9967a 3cb371 87cefa 87ceeb 6a5acd ffd700 708090 bc8f8f 7b68ee 6b8e23 " "a52a2a 7fffd4 ffdead ffdab9 808000 483d8b 20b2aa a020f0 48d1cc db7093 " "a9a9a9 663399 b0e0e6 deb887 7f007f 7fff00 8b008b ffff00 cd5c5c add8e6") #: A set of predefined uniquely-looking colors. __FIXED_COLORS: Final[tuple[tuple[tuple[float, float, float], ...], ...]] = ( __PALETTE_11, __PALETTE_21, __PALETTE_25, __PALETTE_28, __PALETTE_30, __PALETTE_35, __PALETTE_40, __PALETTE_45, __PALETTE_50, __PALETTE_60, __PALETTE_70, __PALETTE_80, __PALETTE_90, __PALETTE_100, __PALETTE_110)
[docs] def distinct_colors(n: int) -> tuple[tuple[float, float, float], ...]: """ Obtain a set of `n` distinct colors. :param n: the number of colors required :return: a tuple of colors """ check_int_range(n, "n", 1, 1000) # First, let us see if we can cover the range with hand-picked colors. for k in __FIXED_COLORS: lk = len(k) if lk >= n: if lk == n: return k return tuple(k[0:n]) # Second, let's see whether the method from # https://stackoverflow.com/questions/8389636 # works. # This method does not seem to make good use of the available color space. # Since we use it only for cases with more than 110 colors, that's OK. cm = plt.get_cmap("gist_rainbow") c_norm = colors.Normalize(vmin=0, vmax=n - 1) scalar_map = mplcm.ScalarMappable(norm=c_norm, cmap=cm) qq = cast(list[tuple[float, float, float]], [tuple(scalar_map.to_rgba(i)[0:3]) for i in np.arange(n)]) ss = set(qq) if len(ss) == n: return tuple(qq) raise ValueError(f"Could not obtain {n} distinct colors.")
#: The solid line dash LINE_DASH_SOLID: Final[str] = "solid" #: An internal array of fixed line styles. __FIXED_LINE_DASHES: \ Final[tuple[str | tuple[float, tuple[float, ...]], ...]] = \ (LINE_DASH_SOLID, "dashed", "dashdot", "dotted", (0.0, (3.0, 5.0, 1.0, 5.0, 1.0, 5.0)), # dashdotdotted (0.0, (3.0, 1.0, 1.0, 1.0)), # densely dashdotted (0.0, (5.0, 1.0)), # densely dashed (0.0, (1.0, 1.0)), # densely dotted (0.0, (3.0, 1.0, 1.0, 1.0, 1.0, 1.0)), # densely dashdotdotted (0.0, (1.0, 10.0)), # loosely dotted (0.0, (5.0, 10.0)), # loosely dashed (0.0, (3.0, 10.0, 1.0, 10.0)), # loosely dashdotted (0.0, (3.0, 10.0, 1.0, 10.0, 1.0, 10.0))) # loosely dashdotdotted
[docs] def distinct_line_dashes(n: int) -> \ tuple[str | tuple[float, tuple[float, ...]], ...]: """ Create a sequence of distinct line dashes. :param n: the number of styles :return: the styles """ check_int_range(n, "n", 1, len(__FIXED_LINE_DASHES) - 1) if n == __FIXED_LINE_DASHES: return __FIXED_LINE_DASHES return tuple(__FIXED_LINE_DASHES[0:n])
#: The fixed predefined distinct markers __FIXED_MARKERS: tuple[str, ...] = ("o", "^", "s", "P", "X", "D", "*", "p")
[docs] def distinct_markers(n: int) -> tuple[str, ...]: """ Create a sequence of distinct markers. :param n: the number of markers :return: the markers """ lfm: Final[int] = len(__FIXED_MARKERS) check_int_range(n, "n", 1, lfm) if n == lfm: return __FIXED_MARKERS return tuple(__FIXED_MARKERS[0:n])
[docs] def importance_to_line_width(importance: int) -> float: """ Transform an importance value to a line width. Basically, an importance of `0` indicates a normal line in a normal plot that does not need to be emphasized. A positive importance means that the line should be emphasized. A negative importance means that the line should be de-emphasized. :param importance: a value between -9 and 9 :return: the line width """ check_int_range(importance, "importance", -9, 9) if importance >= 0: return 2.0 * (0.5 + importance) if importance == -1: return 2.0 / 3.0 if importance == -2: return 0.5 return 0.7 ** (-importance)
[docs] def importance_to_alpha(importance: int) -> float: """ Transform an importance value to an alpha value. Basically, an importance of `0` indicates a normal line in a normal plot that does not need to be emphasized. A positive importance means that the line should be emphasized. A negative importance means that the line should be de-emphasized. :param importance: a value between -9 and 9 :return: the alpha """ check_int_range(importance, "importance", -9, 9) if importance >= 0: return 1.0 if importance == -1: return 2.0 / 3.0 if importance == -2: return 0.5 return 1.0 / 3.0
#: The internal default basic style __BASE_LINE_STYLE: Final[dict[str, object]] = { "alpha": 1.0, "antialiased": True, "color": COLOR_BLACK, "dash_capstyle": "butt", "dash_joinstyle": "round", "linestyle": LINE_DASH_SOLID, "linewidth": 1.0, "solid_capstyle": "round", "solid_joinstyle": "round", }
[docs] def create_line_style(**kwargs) -> dict[str, object]: """ Obtain the basic style for lines in diagrams. :param kwargs: any additional overrides :return: a dictionary with the style elements """ res = dict(__BASE_LINE_STYLE) res.update(kwargs) return res
[docs] def importance_to_font_size(importance: float) -> float: """ Transform an importance value to a font size. :param importance: the importance value :return: the font size """ check_int_range(importance, "importance", -9, 9) if importance < 0: return 7.5 if importance <= 0: return 8.0 if importance == 1: return 8.5 if importance == 2: return 9.0 if importance == 3: return 10.0 return 11.0
#: The default grid color GRID_COLOR: Final[tuple[float, float, float]] = \ (7.0 / 11.0, 7.0 / 11.0, 7.0 / 11.0)
[docs] def rgb_to_gray(r: float, g: float, b: float) -> float: """ Convert RGB values to gray scale. :param r: the red value :param g: the green value :param b: the blue value :return: the gray value """ return (0.2989 * r) + (0.5870 * g) + (0.1140 * b)
[docs] def text_color_for_background(background: tuple[float, float, float]) \ -> tuple[float, float, float]: """ Get a reasonable text color for a given background color. :param background: the background color :return: the text color """ br: Final[float] = background[0] bg: Final[float] = background[1] bb: Final[float] = background[2] bgg: Final[float] = rgb_to_gray(br, bg, bb) return COLOR_WHITE if bgg < 0.3 else COLOR_BLACK