1import colorsys
2from itertools import cycle
3
4import numpy as np
5import matplotlib as mpl
6
7from .external import husl
8
9from .utils import desaturate, get_color_cycle
10from .colors import xkcd_rgb, crayons
11
12
13__all__ = ["color_palette", "hls_palette", "husl_palette", "mpl_palette",
14           "dark_palette", "light_palette", "diverging_palette",
15           "blend_palette", "xkcd_palette", "crayon_palette",
16           "cubehelix_palette", "set_color_codes"]
17
18
19SEABORN_PALETTES = dict(
20    deep=["#4C72B0", "#DD8452", "#55A868", "#C44E52", "#8172B3",
21          "#937860", "#DA8BC3", "#8C8C8C", "#CCB974", "#64B5CD"],
22    deep6=["#4C72B0", "#55A868", "#C44E52",
23           "#8172B3", "#CCB974", "#64B5CD"],
24    muted=["#4878D0", "#EE854A", "#6ACC64", "#D65F5F", "#956CB4",
25           "#8C613C", "#DC7EC0", "#797979", "#D5BB67", "#82C6E2"],
26    muted6=["#4878D0", "#6ACC64", "#D65F5F",
27            "#956CB4", "#D5BB67", "#82C6E2"],
28    pastel=["#A1C9F4", "#FFB482", "#8DE5A1", "#FF9F9B", "#D0BBFF",
29            "#DEBB9B", "#FAB0E4", "#CFCFCF", "#FFFEA3", "#B9F2F0"],
30    pastel6=["#A1C9F4", "#8DE5A1", "#FF9F9B",
31             "#D0BBFF", "#FFFEA3", "#B9F2F0"],
32    bright=["#023EFF", "#FF7C00", "#1AC938", "#E8000B", "#8B2BE2",
33            "#9F4800", "#F14CC1", "#A3A3A3", "#FFC400", "#00D7FF"],
34    bright6=["#023EFF", "#1AC938", "#E8000B",
35             "#8B2BE2", "#FFC400", "#00D7FF"],
36    dark=["#001C7F", "#B1400D", "#12711C", "#8C0800", "#591E71",
37          "#592F0D", "#A23582", "#3C3C3C", "#B8850A", "#006374"],
38    dark6=["#001C7F", "#12711C", "#8C0800",
39           "#591E71", "#B8850A", "#006374"],
40    colorblind=["#0173B2", "#DE8F05", "#029E73", "#D55E00", "#CC78BC",
41                "#CA9161", "#FBAFE4", "#949494", "#ECE133", "#56B4E9"],
42    colorblind6=["#0173B2", "#029E73", "#D55E00",
43                 "#CC78BC", "#ECE133", "#56B4E9"]
44)
45
46
47MPL_QUAL_PALS = {
48    "tab10": 10, "tab20": 20, "tab20b": 20, "tab20c": 20,
49    "Set1": 9, "Set2": 8, "Set3": 12,
50    "Accent": 8, "Paired": 12,
51    "Pastel1": 9, "Pastel2": 8, "Dark2": 8,
52}
53
54
55QUAL_PALETTE_SIZES = MPL_QUAL_PALS.copy()
56QUAL_PALETTE_SIZES.update({k: len(v) for k, v in SEABORN_PALETTES.items()})
57QUAL_PALETTES = list(QUAL_PALETTE_SIZES.keys())
58
59
60class _ColorPalette(list):
61    """Set the color palette in a with statement, otherwise be a list."""
62    def __enter__(self):
63        """Open the context."""
64        from .rcmod import set_palette
65        self._orig_palette = color_palette()
66        set_palette(self)
67        return self
68
69    def __exit__(self, *args):
70        """Close the context."""
71        from .rcmod import set_palette
72        set_palette(self._orig_palette)
73
74    def as_hex(self):
75        """Return a color palette with hex codes instead of RGB values."""
76        hex = [mpl.colors.rgb2hex(rgb) for rgb in self]
77        return _ColorPalette(hex)
78
79    def _repr_html_(self):
80        """Rich display of the color palette in an HTML frontend."""
81        s = 55
82        n = len(self)
83        html = f'<svg  width="{n * s}" height="{s}">'
84        for i, c in enumerate(self.as_hex()):
85            html += (
86                f'<rect x="{i * s}" y="0" width="{s}" height="{s}" style="fill:{c};'
87                'stroke-width:2;stroke:rgb(255,255,255)"/>'
88            )
89        html += '</svg>'
90        return html
91
92
93def color_palette(palette=None, n_colors=None, desat=None, as_cmap=False):
94    """Return a list of colors or continuous colormap defining a palette.
95
96    Possible ``palette`` values include:
97        - Name of a seaborn palette (deep, muted, bright, pastel, dark, colorblind)
98        - Name of matplotlib colormap
99        - 'husl' or 'hsl'
100        - 'ch:<cubehelix arguments>'
101        - 'light:<color>', 'dark:<color>', 'blend:<color>,<color>',
102        - A sequence of colors in any format matplotlib accepts
103
104    Calling this function with ``palette=None`` will return the current
105    matplotlib color cycle.
106
107    This function can also be used in a ``with`` statement to temporarily
108    set the color cycle for a plot or set of plots.
109
110    See the :ref:`tutorial <palette_tutorial>` for more information.
111
112    Parameters
113    ----------
114    palette: None, string, or sequence, optional
115        Name of palette or None to return current palette. If a sequence, input
116        colors are used but possibly cycled and desaturated.
117    n_colors : int, optional
118        Number of colors in the palette. If ``None``, the default will depend
119        on how ``palette`` is specified. Named palettes default to 6 colors,
120        but grabbing the current palette or passing in a list of colors will
121        not change the number of colors unless this is specified. Asking for
122        more colors than exist in the palette will cause it to cycle. Ignored
123        when ``as_cmap`` is True.
124    desat : float, optional
125        Proportion to desaturate each color by.
126    as_cmap : bool
127        If True, return a :class:`matplotlib.colors.Colormap`.
128
129    Returns
130    -------
131    list of RGB tuples or :class:`matplotlib.colors.Colormap`
132
133    See Also
134    --------
135    set_palette : Set the default color cycle for all plots.
136    set_color_codes : Reassign color codes like ``"b"``, ``"g"``, etc. to
137                      colors from one of the seaborn palettes.
138
139    Examples
140    --------
141
142    .. include:: ../docstrings/color_palette.rst
143
144    """
145    if palette is None:
146        palette = get_color_cycle()
147        if n_colors is None:
148            n_colors = len(palette)
149
150    elif not isinstance(palette, str):
151        palette = palette
152        if n_colors is None:
153            n_colors = len(palette)
154    else:
155
156        if n_colors is None:
157            # Use all colors in a qualitative palette or 6 of another kind
158            n_colors = QUAL_PALETTE_SIZES.get(palette, 6)
159
160        if palette in SEABORN_PALETTES:
161            # Named "seaborn variant" of matplotlib default color cycle
162            palette = SEABORN_PALETTES[palette]
163
164        elif palette == "hls":
165            # Evenly spaced colors in cylindrical RGB space
166            palette = hls_palette(n_colors, as_cmap=as_cmap)
167
168        elif palette == "husl":
169            # Evenly spaced colors in cylindrical Lab space
170            palette = husl_palette(n_colors, as_cmap=as_cmap)
171
172        elif palette.lower() == "jet":
173            # Paternalism
174            raise ValueError("No.")
175
176        elif palette.startswith("ch:"):
177            # Cubehelix palette with params specified in string
178            args, kwargs = _parse_cubehelix_args(palette)
179            palette = cubehelix_palette(n_colors, *args, **kwargs, as_cmap=as_cmap)
180
181        elif palette.startswith("light:"):
182            # light palette to color specified in string
183            _, color = palette.split(":")
184            reverse = color.endswith("_r")
185            if reverse:
186                color = color[:-2]
187            palette = light_palette(color, n_colors, reverse=reverse, as_cmap=as_cmap)
188
189        elif palette.startswith("dark:"):
190            # light palette to color specified in string
191            _, color = palette.split(":")
192            reverse = color.endswith("_r")
193            if reverse:
194                color = color[:-2]
195            palette = dark_palette(color, n_colors, reverse=reverse, as_cmap=as_cmap)
196
197        elif palette.startswith("blend:"):
198            # blend palette between colors specified in string
199            _, colors = palette.split(":")
200            colors = colors.split(",")
201            palette = blend_palette(colors, n_colors, as_cmap=as_cmap)
202
203        else:
204            try:
205                # Perhaps a named matplotlib colormap?
206                palette = mpl_palette(palette, n_colors, as_cmap=as_cmap)
207            except ValueError:
208                raise ValueError("%s is not a valid palette name" % palette)
209
210    if desat is not None:
211        palette = [desaturate(c, desat) for c in palette]
212
213    if not as_cmap:
214
215        # Always return as many colors as we asked for
216        pal_cycle = cycle(palette)
217        palette = [next(pal_cycle) for _ in range(n_colors)]
218
219        # Always return in r, g, b tuple format
220        try:
221            palette = map(mpl.colors.colorConverter.to_rgb, palette)
222            palette = _ColorPalette(palette)
223        except ValueError:
224            raise ValueError(f"Could not generate a palette for {palette}")
225
226    return palette
227
228
229def hls_palette(n_colors=6, h=.01, l=.6, s=.65, as_cmap=False):  # noqa
230    """Get a set of evenly spaced colors in HLS hue space.
231
232    h, l, and s should be between 0 and 1
233
234    Parameters
235    ----------
236
237    n_colors : int
238        number of colors in the palette
239    h : float
240        first hue
241    l : float
242        lightness
243    s : float
244        saturation
245
246    Returns
247    -------
248    list of RGB tuples or :class:`matplotlib.colors.Colormap`
249
250    See Also
251    --------
252    husl_palette : Make a palette using evenly spaced hues in the HUSL system.
253
254    Examples
255    --------
256
257    Create a palette of 10 colors with the default parameters:
258
259    .. plot::
260        :context: close-figs
261
262        >>> import seaborn as sns; sns.set_theme()
263        >>> sns.palplot(sns.hls_palette(10))
264
265    Create a palette of 10 colors that begins at a different hue value:
266
267    .. plot::
268        :context: close-figs
269
270        >>> sns.palplot(sns.hls_palette(10, h=.5))
271
272    Create a palette of 10 colors that are darker than the default:
273
274    .. plot::
275        :context: close-figs
276
277        >>> sns.palplot(sns.hls_palette(10, l=.4))
278
279    Create a palette of 10 colors that are less saturated than the default:
280
281    .. plot::
282        :context: close-figs
283
284        >>> sns.palplot(sns.hls_palette(10, s=.4))
285
286    """
287    if as_cmap:
288        n_colors = 256
289    hues = np.linspace(0, 1, int(n_colors) + 1)[:-1]
290    hues += h
291    hues %= 1
292    hues -= hues.astype(int)
293    palette = [colorsys.hls_to_rgb(h_i, l, s) for h_i in hues]
294    if as_cmap:
295        return mpl.colors.ListedColormap(palette, "hls")
296    else:
297        return _ColorPalette(palette)
298
299
300def husl_palette(n_colors=6, h=.01, s=.9, l=.65, as_cmap=False):  # noqa
301    """Get a set of evenly spaced colors in HUSL hue space.
302
303    h, s, and l should be between 0 and 1
304
305    Parameters
306    ----------
307
308    n_colors : int
309        number of colors in the palette
310    h : float
311        first hue
312    s : float
313        saturation
314    l : float
315        lightness
316
317    Returns
318    -------
319    list of RGB tuples or :class:`matplotlib.colors.Colormap`
320
321    See Also
322    --------
323    hls_palette : Make a palette using evently spaced circular hues in the
324                  HSL system.
325
326    Examples
327    --------
328
329    Create a palette of 10 colors with the default parameters:
330
331    .. plot::
332        :context: close-figs
333
334        >>> import seaborn as sns; sns.set_theme()
335        >>> sns.palplot(sns.husl_palette(10))
336
337    Create a palette of 10 colors that begins at a different hue value:
338
339    .. plot::
340        :context: close-figs
341
342        >>> sns.palplot(sns.husl_palette(10, h=.5))
343
344    Create a palette of 10 colors that are darker than the default:
345
346    .. plot::
347        :context: close-figs
348
349        >>> sns.palplot(sns.husl_palette(10, l=.4))
350
351    Create a palette of 10 colors that are less saturated than the default:
352
353    .. plot::
354        :context: close-figs
355
356        >>> sns.palplot(sns.husl_palette(10, s=.4))
357
358    """
359    if as_cmap:
360        n_colors = 256
361    hues = np.linspace(0, 1, int(n_colors) + 1)[:-1]
362    hues += h
363    hues %= 1
364    hues *= 359
365    s *= 99
366    l *= 99  # noqa
367    palette = [_color_to_rgb((h_i, s, l), input="husl") for h_i in hues]
368    if as_cmap:
369        return mpl.colors.ListedColormap(palette, "hsl")
370    else:
371        return _ColorPalette(palette)
372
373
374def mpl_palette(name, n_colors=6, as_cmap=False):
375    """Return discrete colors from a matplotlib palette.
376
377    Note that this handles the qualitative colorbrewer palettes
378    properly, although if you ask for more colors than a particular
379    qualitative palette can provide you will get fewer than you are
380    expecting. In contrast, asking for qualitative color brewer palettes
381    using :func:`color_palette` will return the expected number of colors,
382    but they will cycle.
383
384    If you are using the IPython notebook, you can also use the function
385    :func:`choose_colorbrewer_palette` to interactively select palettes.
386
387    Parameters
388    ----------
389    name : string
390        Name of the palette. This should be a named matplotlib colormap.
391    n_colors : int
392        Number of discrete colors in the palette.
393
394    Returns
395    -------
396    list of RGB tuples or :class:`matplotlib.colors.Colormap`
397
398    Examples
399    --------
400
401    Create a qualitative colorbrewer palette with 8 colors:
402
403    .. plot::
404        :context: close-figs
405
406        >>> import seaborn as sns; sns.set_theme()
407        >>> sns.palplot(sns.mpl_palette("Set2", 8))
408
409    Create a sequential colorbrewer palette:
410
411    .. plot::
412        :context: close-figs
413
414        >>> sns.palplot(sns.mpl_palette("Blues"))
415
416    Create a diverging palette:
417
418    .. plot::
419        :context: close-figs
420
421        >>> sns.palplot(sns.mpl_palette("seismic", 8))
422
423    Create a "dark" sequential palette:
424
425    .. plot::
426        :context: close-figs
427
428        >>> sns.palplot(sns.mpl_palette("GnBu_d"))
429
430    """
431    if name.endswith("_d"):
432        sub_name = name[:-2]
433        if sub_name.endswith("_r"):
434            reverse = True
435            sub_name = sub_name[:-2]
436        else:
437            reverse = False
438        pal = color_palette(sub_name, 2) + ["#333333"]
439        if reverse:
440            pal = pal[::-1]
441        cmap = blend_palette(pal, n_colors, as_cmap=True)
442    else:
443        cmap = mpl.cm.get_cmap(name)
444
445    if name in MPL_QUAL_PALS:
446        bins = np.linspace(0, 1, MPL_QUAL_PALS[name])[:n_colors]
447    else:
448        bins = np.linspace(0, 1, int(n_colors) + 2)[1:-1]
449    palette = list(map(tuple, cmap(bins)[:, :3]))
450
451    if as_cmap:
452        return cmap
453    else:
454        return _ColorPalette(palette)
455
456
457def _color_to_rgb(color, input):
458    """Add some more flexibility to color choices."""
459    if input == "hls":
460        color = colorsys.hls_to_rgb(*color)
461    elif input == "husl":
462        color = husl.husl_to_rgb(*color)
463        color = tuple(np.clip(color, 0, 1))
464    elif input == "xkcd":
465        color = xkcd_rgb[color]
466
467    return mpl.colors.to_rgb(color)
468
469
470def dark_palette(color, n_colors=6, reverse=False, as_cmap=False, input="rgb"):
471    """Make a sequential palette that blends from dark to ``color``.
472
473    This kind of palette is good for data that range between relatively
474    uninteresting low values and interesting high values.
475
476    The ``color`` parameter can be specified in a number of ways, including
477    all options for defining a color in matplotlib and several additional
478    color spaces that are handled by seaborn. You can also use the database
479    of named colors from the XKCD color survey.
480
481    If you are using the IPython notebook, you can also choose this palette
482    interactively with the :func:`choose_dark_palette` function.
483
484    Parameters
485    ----------
486    color : base color for high values
487        hex, rgb-tuple, or html color name
488    n_colors : int, optional
489        number of colors in the palette
490    reverse : bool, optional
491        if True, reverse the direction of the blend
492    as_cmap : bool, optional
493        If True, return a :class:`matplotlib.colors.Colormap`.
494    input : {'rgb', 'hls', 'husl', xkcd'}
495        Color space to interpret the input color. The first three options
496        apply to tuple inputs and the latter applies to string inputs.
497
498    Returns
499    -------
500    list of RGB tuples or :class:`matplotlib.colors.Colormap`
501
502    See Also
503    --------
504    light_palette : Create a sequential palette with bright low values.
505    diverging_palette : Create a diverging palette with two colors.
506
507    Examples
508    --------
509
510    Generate a palette from an HTML color:
511
512    .. plot::
513        :context: close-figs
514
515        >>> import seaborn as sns; sns.set_theme()
516        >>> sns.palplot(sns.dark_palette("purple"))
517
518    Generate a palette that decreases in lightness:
519
520    .. plot::
521        :context: close-figs
522
523        >>> sns.palplot(sns.dark_palette("seagreen", reverse=True))
524
525    Generate a palette from an HUSL-space seed:
526
527    .. plot::
528        :context: close-figs
529
530        >>> sns.palplot(sns.dark_palette((260, 75, 60), input="husl"))
531
532    Generate a colormap object:
533
534    .. plot::
535        :context: close-figs
536
537        >>> from numpy import arange
538        >>> x = arange(25).reshape(5, 5)
539        >>> cmap = sns.dark_palette("#2ecc71", as_cmap=True)
540        >>> ax = sns.heatmap(x, cmap=cmap)
541
542    """
543    rgb = _color_to_rgb(color, input)
544    h, s, l = husl.rgb_to_husl(*rgb)
545    gray_s, gray_l = .15 * s, 15
546    gray = _color_to_rgb((h, gray_s, gray_l), input="husl")
547    colors = [rgb, gray] if reverse else [gray, rgb]
548    return blend_palette(colors, n_colors, as_cmap)
549
550
551def light_palette(color, n_colors=6, reverse=False, as_cmap=False, input="rgb"):
552    """Make a sequential palette that blends from light to ``color``.
553
554    This kind of palette is good for data that range between relatively
555    uninteresting low values and interesting high values.
556
557    The ``color`` parameter can be specified in a number of ways, including
558    all options for defining a color in matplotlib and several additional
559    color spaces that are handled by seaborn. You can also use the database
560    of named colors from the XKCD color survey.
561
562    If you are using the IPython notebook, you can also choose this palette
563    interactively with the :func:`choose_light_palette` function.
564
565    Parameters
566    ----------
567    color : base color for high values
568        hex code, html color name, or tuple in ``input`` space.
569    n_colors : int, optional
570        number of colors in the palette
571    reverse : bool, optional
572        if True, reverse the direction of the blend
573    as_cmap : bool, optional
574        If True, return a :class:`matplotlib.colors.Colormap`.
575    input : {'rgb', 'hls', 'husl', xkcd'}
576        Color space to interpret the input color. The first three options
577        apply to tuple inputs and the latter applies to string inputs.
578
579    Returns
580    -------
581    list of RGB tuples or :class:`matplotlib.colors.Colormap`
582
583    See Also
584    --------
585    dark_palette : Create a sequential palette with dark low values.
586    diverging_palette : Create a diverging palette with two colors.
587
588    Examples
589    --------
590
591    Generate a palette from an HTML color:
592
593    .. plot::
594        :context: close-figs
595
596        >>> import seaborn as sns; sns.set_theme()
597        >>> sns.palplot(sns.light_palette("purple"))
598
599    Generate a palette that increases in lightness:
600
601    .. plot::
602        :context: close-figs
603
604        >>> sns.palplot(sns.light_palette("seagreen", reverse=True))
605
606    Generate a palette from an HUSL-space seed:
607
608    .. plot::
609        :context: close-figs
610
611        >>> sns.palplot(sns.light_palette((260, 75, 60), input="husl"))
612
613    Generate a colormap object:
614
615    .. plot::
616        :context: close-figs
617
618        >>> from numpy import arange
619        >>> x = arange(25).reshape(5, 5)
620        >>> cmap = sns.light_palette("#2ecc71", as_cmap=True)
621        >>> ax = sns.heatmap(x, cmap=cmap)
622
623    """
624    rgb = _color_to_rgb(color, input)
625    h, s, l = husl.rgb_to_husl(*rgb)
626    gray_s, gray_l = .15 * s, 95
627    gray = _color_to_rgb((h, gray_s, gray_l), input="husl")
628    colors = [rgb, gray] if reverse else [gray, rgb]
629    return blend_palette(colors, n_colors, as_cmap)
630
631
632def diverging_palette(h_neg, h_pos, s=75, l=50, sep=1, n=6,  # noqa
633                      center="light", as_cmap=False):
634    """Make a diverging palette between two HUSL colors.
635
636    If you are using the IPython notebook, you can also choose this palette
637    interactively with the :func:`choose_diverging_palette` function.
638
639    Parameters
640    ----------
641    h_neg, h_pos : float in [0, 359]
642        Anchor hues for negative and positive extents of the map.
643    s : float in [0, 100], optional
644        Anchor saturation for both extents of the map.
645    l : float in [0, 100], optional
646        Anchor lightness for both extents of the map.
647    sep : int, optional
648        Size of the intermediate region.
649    n : int, optional
650        Number of colors in the palette (if not returning a cmap)
651    center : {"light", "dark"}, optional
652        Whether the center of the palette is light or dark
653    as_cmap : bool, optional
654        If True, return a :class:`matplotlib.colors.Colormap`.
655
656    Returns
657    -------
658    list of RGB tuples or :class:`matplotlib.colors.Colormap`
659
660    See Also
661    --------
662    dark_palette : Create a sequential palette with dark values.
663    light_palette : Create a sequential palette with light values.
664
665    Examples
666    --------
667
668    Generate a blue-white-red palette:
669
670    .. plot::
671        :context: close-figs
672
673        >>> import seaborn as sns; sns.set_theme()
674        >>> sns.palplot(sns.diverging_palette(240, 10, n=9))
675
676    Generate a brighter green-white-purple palette:
677
678    .. plot::
679        :context: close-figs
680
681        >>> sns.palplot(sns.diverging_palette(150, 275, s=80, l=55, n=9))
682
683    Generate a blue-black-red palette:
684
685    .. plot::
686        :context: close-figs
687
688        >>> sns.palplot(sns.diverging_palette(250, 15, s=75, l=40,
689        ...                                   n=9, center="dark"))
690
691    Generate a colormap object:
692
693    .. plot::
694        :context: close-figs
695
696        >>> from numpy import arange
697        >>> x = arange(25).reshape(5, 5)
698        >>> cmap = sns.diverging_palette(220, 20, as_cmap=True)
699        >>> ax = sns.heatmap(x, cmap=cmap)
700
701    """
702    palfunc = dict(dark=dark_palette, light=light_palette)[center]
703    n_half = int(128 - (sep // 2))
704    neg = palfunc((h_neg, s, l), n_half, reverse=True, input="husl")
705    pos = palfunc((h_pos, s, l), n_half, input="husl")
706    midpoint = dict(light=[(.95, .95, .95)], dark=[(.133, .133, .133)])[center]
707    mid = midpoint * sep
708    pal = blend_palette(np.concatenate([neg, mid, pos]), n, as_cmap=as_cmap)
709    return pal
710
711
712def blend_palette(colors, n_colors=6, as_cmap=False, input="rgb"):
713    """Make a palette that blends between a list of colors.
714
715    Parameters
716    ----------
717    colors : sequence of colors in various formats interpreted by ``input``
718        hex code, html color name, or tuple in ``input`` space.
719    n_colors : int, optional
720        Number of colors in the palette.
721    as_cmap : bool, optional
722        If True, return a :class:`matplotlib.colors.Colormap`.
723
724    Returns
725    -------
726    list of RGB tuples or :class:`matplotlib.colors.Colormap`
727
728    """
729    colors = [_color_to_rgb(color, input) for color in colors]
730    name = "blend"
731    pal = mpl.colors.LinearSegmentedColormap.from_list(name, colors)
732    if not as_cmap:
733        rgb_array = pal(np.linspace(0, 1, int(n_colors)))[:, :3]  # no alpha
734        pal = _ColorPalette(map(tuple, rgb_array))
735    return pal
736
737
738def xkcd_palette(colors):
739    """Make a palette with color names from the xkcd color survey.
740
741    See xkcd for the full list of colors: https://xkcd.com/color/rgb/
742
743    This is just a simple wrapper around the ``seaborn.xkcd_rgb`` dictionary.
744
745    Parameters
746    ----------
747    colors : list of strings
748        List of keys in the ``seaborn.xkcd_rgb`` dictionary.
749
750    Returns
751    -------
752    palette : seaborn color palette
753        Returns the list of colors as RGB tuples in an object that behaves like
754        other seaborn color palettes.
755
756    See Also
757    --------
758    crayon_palette : Make a palette with Crayola crayon colors.
759
760    """
761    palette = [xkcd_rgb[name] for name in colors]
762    return color_palette(palette, len(palette))
763
764
765def crayon_palette(colors):
766    """Make a palette with color names from Crayola crayons.
767
768    Colors are taken from here:
769    https://en.wikipedia.org/wiki/List_of_Crayola_crayon_colors
770
771    This is just a simple wrapper around the ``seaborn.crayons`` dictionary.
772
773    Parameters
774    ----------
775    colors : list of strings
776        List of keys in the ``seaborn.crayons`` dictionary.
777
778    Returns
779    -------
780    palette : seaborn color palette
781        Returns the list of colors as rgb tuples in an object that behaves like
782        other seaborn color palettes.
783
784    See Also
785    --------
786    xkcd_palette : Make a palette with named colors from the XKCD color survey.
787
788    """
789    palette = [crayons[name] for name in colors]
790    return color_palette(palette, len(palette))
791
792
793def cubehelix_palette(n_colors=6, start=0, rot=.4, gamma=1.0, hue=0.8,
794                      light=.85, dark=.15, reverse=False, as_cmap=False):
795    """Make a sequential palette from the cubehelix system.
796
797    This produces a colormap with linearly-decreasing (or increasing)
798    brightness. That means that information will be preserved if printed to
799    black and white or viewed by someone who is colorblind.  "cubehelix" is
800    also available as a matplotlib-based palette, but this function gives the
801    user more control over the look of the palette and has a different set of
802    defaults.
803
804    In addition to using this function, it is also possible to generate a
805    cubehelix palette generally in seaborn using a string-shorthand; see the
806    example below.
807
808    Parameters
809    ----------
810    n_colors : int
811        Number of colors in the palette.
812    start : float, 0 <= start <= 3
813        The hue at the start of the helix.
814    rot : float
815        Rotations around the hue wheel over the range of the palette.
816    gamma : float 0 <= gamma
817        Gamma factor to emphasize darker (gamma < 1) or lighter (gamma > 1)
818        colors.
819    hue : float, 0 <= hue <= 1
820        Saturation of the colors.
821    dark : float 0 <= dark <= 1
822        Intensity of the darkest color in the palette.
823    light : float 0 <= light <= 1
824        Intensity of the lightest color in the palette.
825    reverse : bool
826        If True, the palette will go from dark to light.
827    as_cmap : bool
828        If True, return a :class:`matplotlib.colors.Colormap`.
829
830    Returns
831    -------
832    list of RGB tuples or :class:`matplotlib.colors.Colormap`
833
834    See Also
835    --------
836    choose_cubehelix_palette : Launch an interactive widget to select cubehelix
837                               palette parameters.
838    dark_palette : Create a sequential palette with dark low values.
839    light_palette : Create a sequential palette with bright low values.
840
841    References
842    ----------
843    Green, D. A. (2011). "A colour scheme for the display of astronomical
844    intensity images". Bulletin of the Astromical Society of India, Vol. 39,
845    p. 289-295.
846
847    Examples
848    --------
849
850    Generate the default palette:
851
852    .. plot::
853        :context: close-figs
854
855        >>> import seaborn as sns; sns.set_theme()
856        >>> sns.palplot(sns.cubehelix_palette())
857
858    Rotate backwards from the same starting location:
859
860    .. plot::
861        :context: close-figs
862
863        >>> sns.palplot(sns.cubehelix_palette(rot=-.4))
864
865    Use a different starting point and shorter rotation:
866
867    .. plot::
868        :context: close-figs
869
870        >>> sns.palplot(sns.cubehelix_palette(start=2.8, rot=.1))
871
872    Reverse the direction of the lightness ramp:
873
874    .. plot::
875        :context: close-figs
876
877        >>> sns.palplot(sns.cubehelix_palette(reverse=True))
878
879    Generate a colormap object:
880
881    .. plot::
882        :context: close-figs
883
884        >>> from numpy import arange
885        >>> x = arange(25).reshape(5, 5)
886        >>> cmap = sns.cubehelix_palette(as_cmap=True)
887        >>> ax = sns.heatmap(x, cmap=cmap)
888
889    Use the full lightness range:
890
891    .. plot::
892        :context: close-figs
893
894        >>> cmap = sns.cubehelix_palette(dark=0, light=1, as_cmap=True)
895        >>> ax = sns.heatmap(x, cmap=cmap)
896
897    Use through the :func:`color_palette` interface:
898
899    .. plot::
900        :context: close-figs
901
902        >>> sns.palplot(sns.color_palette("ch:2,r=.2,l=.6"))
903
904    """
905    def get_color_function(p0, p1):
906        # Copied from matplotlib because it lives in private module
907        def color(x):
908            # Apply gamma factor to emphasise low or high intensity values
909            xg = x ** gamma
910
911            # Calculate amplitude and angle of deviation from the black
912            # to white diagonal in the plane of constant
913            # perceived intensity.
914            a = hue * xg * (1 - xg) / 2
915
916            phi = 2 * np.pi * (start / 3 + rot * x)
917
918            return xg + a * (p0 * np.cos(phi) + p1 * np.sin(phi))
919        return color
920
921    cdict = {
922        "red": get_color_function(-0.14861, 1.78277),
923        "green": get_color_function(-0.29227, -0.90649),
924        "blue": get_color_function(1.97294, 0.0),
925    }
926
927    cmap = mpl.colors.LinearSegmentedColormap("cubehelix", cdict)
928
929    x = np.linspace(light, dark, int(n_colors))
930    pal = cmap(x)[:, :3].tolist()
931    if reverse:
932        pal = pal[::-1]
933
934    if as_cmap:
935        x_256 = np.linspace(light, dark, 256)
936        if reverse:
937            x_256 = x_256[::-1]
938        pal_256 = cmap(x_256)
939        cmap = mpl.colors.ListedColormap(pal_256, "seaborn_cubehelix")
940        return cmap
941    else:
942        return _ColorPalette(pal)
943
944
945def _parse_cubehelix_args(argstr):
946    """Turn stringified cubehelix params into args/kwargs."""
947
948    if argstr.startswith("ch:"):
949        argstr = argstr[3:]
950
951    if argstr.endswith("_r"):
952        reverse = True
953        argstr = argstr[:-2]
954    else:
955        reverse = False
956
957    if not argstr:
958        return [], {"reverse": reverse}
959
960    all_args = argstr.split(",")
961
962    args = [float(a.strip(" ")) for a in all_args if "=" not in a]
963
964    kwargs = [a.split("=") for a in all_args if "=" in a]
965    kwargs = {k.strip(" "): float(v.strip(" ")) for k, v in kwargs}
966
967    kwarg_map = dict(
968        s="start", r="rot", g="gamma",
969        h="hue", l="light", d="dark",  # noqa: E741
970    )
971
972    kwargs = {kwarg_map.get(k, k): v for k, v in kwargs.items()}
973
974    if reverse:
975        kwargs["reverse"] = True
976
977    return args, kwargs
978
979
980def set_color_codes(palette="deep"):
981    """Change how matplotlib color shorthands are interpreted.
982
983    Calling this will change how shorthand codes like "b" or "g"
984    are interpreted by matplotlib in subsequent plots.
985
986    Parameters
987    ----------
988    palette : {deep, muted, pastel, dark, bright, colorblind}
989        Named seaborn palette to use as the source of colors.
990
991    See Also
992    --------
993    set : Color codes can be set through the high-level seaborn style
994          manager.
995    set_palette : Color codes can also be set through the function that
996                  sets the matplotlib color cycle.
997
998    Examples
999    --------
1000
1001    Map matplotlib color codes to the default seaborn palette.
1002
1003    .. plot::
1004        :context: close-figs
1005
1006        >>> import matplotlib.pyplot as plt
1007        >>> import seaborn as sns; sns.set_theme()
1008        >>> sns.set_color_codes()
1009        >>> _ = plt.plot([0, 1], color="r")
1010
1011    Use a different seaborn palette.
1012
1013    .. plot::
1014        :context: close-figs
1015
1016        >>> sns.set_color_codes("dark")
1017        >>> _ = plt.plot([0, 1], color="g")
1018        >>> _ = plt.plot([0, 2], color="m")
1019
1020    """
1021    if palette == "reset":
1022        colors = [(0., 0., 1.), (0., .5, 0.), (1., 0., 0.), (.75, 0., .75),
1023                  (.75, .75, 0.), (0., .75, .75), (0., 0., 0.)]
1024    elif not isinstance(palette, str):
1025        err = "set_color_codes requires a named seaborn palette"
1026        raise TypeError(err)
1027    elif palette in SEABORN_PALETTES:
1028        if not palette.endswith("6"):
1029            palette = palette + "6"
1030        colors = SEABORN_PALETTES[palette] + [(.1, .1, .1)]
1031    else:
1032        err = "Cannot set colors with palette '{}'".format(palette)
1033        raise ValueError(err)
1034
1035    for code, color in zip("bgrmyck", colors):
1036        rgb = mpl.colors.colorConverter.to_rgb(color)
1037        mpl.colors.colorConverter.colors[code] = rgb
1038        mpl.colors.colorConverter.cache[code] = rgb
1039