HCL-Based Color Palettes¶
As motivated in the previous article (Color Spaces: Classes and Utilities), the HCL space is particularly useful for specifying individual colors and color palettes, as its three axes match those of the human visual system very well. Therefore, the colorspace package provides three types of palettes based on the HCL model:
Qualitative: Designed for coding categorical information, i.e., where no particular ordering of categories is available and every color should receive the same perceptual weight.
Sequential: Designed for coding ordered/numeric information, i.e., going from high to low (or vice versa).
Diverging: Designed for coding ordered/numeric information around a central neutral value, i.e., where colors diverge from neutral to two extremes.
The corresponding classes are qualitative_hcl
,
sequential_hcl
, and diverging_hcl
.
Their construction principles are exemplified in the following
color swatches and explained in more detail below. The desaturated palettes
bring out clearly that luminance differences (light-dark contrasts) are crucial
for sequential and diverging palettes while qualitative palettes are balanced
at the same luminance.
(Source code
, png
, hires.png
, pdf
)
More details about the construction of such palettes is provided in the following while the article on Palette Visualization and Assessment introduces further tools to better understand the properties of color palettes.
To facilitate obtaining good sets of colors, HCL parameter combinations that
yield useful palettes are accessible by name. These can be listed using the
function hcl_palettes()
:
In [1]: from colorspace import hcl_palettes
In [2]: hcl_palettes()
Out[2]:
HCL palettes
Type: Basic: Sequential (single-hue)
Names: Grays, Light Grays, Blues 2, Purples 2, Reds 2
Greens 2
Type: Basic: Sequential (multi-hue)
Names: Purple-Blue, Red-Purple, Red-Blue, Purple-Orange
Blue-Yellow, Green-Yellow, Red-Yellow, Heat, Heat 2
Terrain, Terrain 2, Viridis, Plasma, Dark Mint, Mint
Emrld, BluYl, ag_GrnYl, Peach, PinkYl
Type: Basic: Qualitative
Names: Pastel 1, Dark 2, Dark 3, Set 2, Set 3, Warm, Cold
Harmonic, Dynamic
Type: Basic: Diverging
Names: Blue-Red, Blue-Red 2, Blue-Yellow 2, Blue-Yellow 3
Green-Orange, Cyan-Magenta, Tropic
Type: Advanced: Sequential (single-hue)
Names: Blues 3, Purples 3, Reds 3, Greens 3, Oslo
Type: Advanced: Sequential (multi-hue)
Names: Purple-Yellow, Inferno, Rocket, Mako, BluGrn, Teal
TealGrn, Burg, BurgYl, RedOr, OrYel, Purp, PurpOr
Sunset, Magenta, SunsetDark, ag_Sunset, BrwnYl, YlOrRd
YlOrBr, OrRd, Oranges, YlGn, YlGnBu, Reds, RdPu, PuRd
Purples, PuBuGn, PuBu, Greens, BuGn, GnBu, BuPu, Blues
Lajolla, Turku, Hawaii, Batlow
Type: Advanced: DivergingX
Names: ArmyRose, Earth, Fall, Geyser, TealRose, Temps, PuOr
RdBu, RdGy, PiYG, PRGn, BrBG, RdYlBu, RdYlGn, Spectral
Zissou 1, Cividis, Roma
Type: Advanced: Diverging
Names: ArmyRose, Earth, Fall, Geyser, TealRose, Temps, PuOr
RdBu, RdGy, PiYG, PRGn, BrBG, RdYlBu, RdYlGn, Spectral
Zissou 1, Cividis, Roma, Blue-Red 3, Red-Green
Purple-Green, Purple-Brown, Green-Brown, Blue-Yellow 2
Blue-Yellow 3, Broc, Cork, Vik, Berlin, Lisbon, Tofino
To inspect the HCL parameter combinations for a specific palette simply include
the palette name where upper- vs. lower-case, spaces, etc. are ignored for
matching the label, e.g., "set2"
matches "Set 2"
as well as "SET2"
will.
In [3]: from colorspace import hcl_palettes
In [4]: pal = hcl_palettes().get_palette(name = "SET2")
In [5]: print(pal)
['c1', 'desc', 'fixup', 'gui', 'h1', 'h2', 'l1']
Palette Name: Set 2
Type: Basic: Qualitative
Inspired by: ...
c1 60
fixup True
gui 1
h1 0
h2 360
l1 70
In [6]: print(pal.get_settings())
{'desc': '...', 'h1': 0, 'h2': 360, 'c1': 60, 'l1': 70, 'fixup': True, 'gui': 1}
Calling qualitative_hcl
, sequential_hcl
, and
diverging_hcl
respectively, will return an object of class
hclpalette
defined by a series of parameters which specify
the color palette.
All parameters can either be specified “by hand”
through the HCL parameters, an entire palette can be specified “by name”, or
the name-based specification can be modified by a few HCL parameters. In case
of the HCL parameters, either a vector-based specification such as
h = [0, 270]
or individual parameters h1 = 0
and h2 = 270
can be used.
To compute the actual color hex codes (representing sRGB coordinates), the
method hclpalette.colors()
or hclpalette.__call__()
are used
which return a list of \(N\) colors along the coordinates defined by parameters
specified when constructing the object.
The first three of the following commands lead to equivalent output. The fourth
command yields a modified set of colors (lighter due to a luminance of 80
instead of 70
).
In [7]: from colorspace import qualitative_hcl
In [8]: qualitative_hcl(h = [0, 270], c = 60, l = 70)(4)
Out[8]: ['#ED90A4', '#ABB150', '#00C1B2', '#ACA2EC']
In [9]: qualitative_hcl(h1 = 0, h2 = 270, c1 = 60, l1 = 70)(4)
Out[9]: ['#ED90A4', '#ABB150', '#00C1B2', '#ACA2EC']
In [10]: qualitative_hcl("set2").colors(4)
Out[10]: ['#ED90A4', '#7EBA68', '#6FB1E7', '#ED90A4']
In [11]: qualitative_hcl("Set 2", l = 80).colors(4)
Out[11]: ['#ED90A4', '#7EBA68', '#6FB1E7', '#ED90A4']
Todo
Incorrect description. Missing support for the lambda function which, here,
controls h2
by default. Once implemented please check code/result/text.
Qualitative palettes¶
qualitative_hcl
distinguishes
the underlying categories by a sequence of hues while keeping both chroma and
luminance constant, to give each color in the resulting palette the same
perceptual weight. Thus, h
should be a pair of hues (or equivalently h1
and h2
can be used) with the starting and ending hue of the palette. Then, an
equidistant sequence between these hues is employed, by default spanning the
full color wheel (i.e., the full 360
degrees). Chroma c
(or equivalently c1
)
and luminance l
(or equivalently l1
) are constants. Finally, fixup indicates
whether colors with out-of-range coordinates should be corrected.
In the following graphic the available named palettes are shown. The first five
palettes are close to the ColorBrewer.org palettes of the same name
[HCL2].
They employ different levels of chroma and luminance and, by default, span the
full hue range. The remaining four palettes are taken from
Ihaka [HCL3]. They are based on the same chroma (50
) and
luminance (70
) but the hue is restricted to different intervals.
In [12]: from colorspace import hcl_palettes
In [13]: hcl_palettes(5, "Qualitative", plot = True, ncol = 1)
Out[13]: False
When palettes are employed for shading areas in statistical displays (e.g., in bar plots, pie charts, or regions in maps), lighter colors (with moderate chroma and high luminance) such as “Pastel 1” or “Set 3” are typically less distracting. By contrast, when coloring points or lines, more flashy colors (with high chroma) are often required: On a white background a moderate luminance as in “Dark 2” or “Dark 3” usually works better while on a black/dark background the luminance should be higher as in “Set 2” for example.
Sequential palettes (single-hue)¶
sequential_hcl
codes the underlying numeric values by a monotonic
sequence of increasing (or decreasing) luminance. Thus, the function’s l
argument should provide a vector of length \(2\) with starting and ending luminance
(equivalently, l1
and l2
can be used). Without chroma (i.e., c = 0
),
this simply corresponds to a gray-scale palette, see “Grays” and “Light Grays”
below.
In [14]: from colorspace import hcl_palettes
In [15]: hcl_palettes(7, "Sequential (single-hue)", plot = True,
....: ncol = 1, figsize = (6, 7.5))
....:
Out[15]: False
All except the last are inspired by the ColorBrewer.org palettes with the same base name [HCL2] but restricted to a single hue only. They are intended for a white/light background. The last palette (Oslo) is taken from the scientific color maps of Crameri [HCL1] and is intended for a black/dark background and hence the order is reversed starting from a light blue (not a light gray).
To distinguish many colors in a sequential palette it is important to have a strong contrast on the luminance axis, possibly enhanced by an accompanying pronounced variation in chroma. When only a few colors are needed (e.g., for coding an ordinal categorical variable with few levels) then a lower luminance contrast may suffice.
Sequential palettes (multi-hue)¶
To not only bring out extreme colors in a sequential palette but also better
distinguish middle colors it is a common strategy to employ a sequence of hues.
Thus, the basis of such a palette is still a monotonic luminance sequence as
above (combined with a monotonic or triangular chroma sequence). But rather
than using a single hue, an interval of hues in h
(or beginning hue h1
and
ending hue h2
) can be specified.
sequential_hcl
allows combined variations in hue (h
and
h1
/h2
, respectively), chroma (c
and c1
/c2
/cmax
,
respectively), luminance (l
and l1
/l2
, respectively), and power
transformations for the chroma and luminance trajectories (power and
p1
/p2
, respectively). This yields a broad variety of sequential
palettes, including many that closely match other well-known color palettes.
The plot below shows all the named multi-hue sequential palettes in colorspace
which consist of various palettes created during the development of colorspace,
e.g., by Zeileis et al. [HCL6] or
Stauffer et al. [HCL5] among others.
In addition palettes are provided which closely match the palettes developed by Smith and Van der Walt [HCL4] for matplotlib, matching CARTO palettes [HCL7], ColorBrewer.org palettes [HCL2], and palettes closely matching the scientific palettes by Crameri [HCL1].
In [16]: hcl_palettes(7, "Sequential (multi-hue)", plot = True,
....: ncol = 3, figsize = (10, 8))
....:
Out[16]: False
Note that the palettes differ substantially in the amount of chroma and luminance contrasts. For example, many palettes go from a dark high-chroma color to a neutral low-chroma color (e.g., “Reds”, “Purples”, “Greens”, “Blues”) or even light gray (e.g., “Purple-Blue”). But some palettes also employ relatively high chroma throughout the palette (e.g., the viridis and many CARTO palettes). To emphasize the extremes the former strategy is typically more suitable while the latter works better if all values along the sequence should receive some more perceptual weight.
Diverging palettes¶
diverging_hcl
codes the underlying numeric values by a triangular
luminance sequence with different hues in the left and in the right “arms” of
the palette. Thus, it can be seen as a combination of two sequential palettes
with some restrictions: (a) a single hue is used for each arm of the palette,
(b) chroma and luminance trajectory are balanced between the two arms, (c) the
neutral central value has zero chroma. To specify such a palette a vector of
two hues h (or equivalently h1
and h2
), either a single chroma value
c
(or c1
) or a vector of two chroma values c
(or c1
and
cmax
), a vector of two luminances l
(or l1
and l2
), and power
parameter(s) power (or p1
and p2
) are used.
For more flexible diverging palettes without the restrictions above (and
consequently more parameters) see the divergingx_hcl
palettes
introduced below.
The plot below shows all such diverging palettes that have been named in colorspace:
“Blue-Red” to “Cyan-Magenta” have been developed for colorspace starting from Zeileis, Hornik, and Murrell (2009), taking inspiration from various other palettes, including more balanced and simplified versions of several ColorBrewer.org palettes [HCL2].
“Tropic” closely matches the palette of the same name from CARTO [HCL7].
“Broc” to “Vik” and “Berlin” to “Tofino” closely match the scientific color maps of the same name by Crameri [HCL1], where the first three are intended for a white/light background and the other three for a black/dark background.
Todo
I do not have the same order as I do not mix basic and advanced. Thus, this list above is invalid for the python package. Fix this.
In [17]: hcl_palettes(7, "Diverging", plot = True,
....: ncol = 1, figsize = (6, 10))
....:
Out[17]: False
When choosing a particular palette for a display similar considerations apply as for the sequential palettes. Thus, large luminance differences are important when many colors are used while smaller luminance contrasts may suffice for palettes with fewer colors etc.
Construction details¶
The three different types of palettes (qualitative, sequential, and diverging) are all constructed by combining three different types of trajectories (constant, linear, triangular) for the three different coordinates (hue H, chroma C, luminance L):
Type |
H |
C |
L |
Qualitative |
Linear |
Constant |
Constant |
Sequential
|
Constant (single-hue)
or Linear (multi-hue)
|
Linear (+ power)
or Triangular (+ power)
|
Linear (+ power)
|
Diverging
|
Constant (2x)
|
Linear (+ power)
or Triangular (+ power)
|
Linear (+ power)
|
As pointed out initially in this article, luminance is probably the most important property for defining the type of palette. It is constant for qualitative palettes, monotonic for sequential palettes (linear or a power transformation), and uses two monotonic trajectories (linear or a power transformation) diverging from the same neutral value.
Hue trajectories are also rather intuitive and straightforward for the three
different types of palettes. However, chroma trajectories are probably the most
complicated and least obvious from the examples above. Hence, the exact
mathematical equations underlying the chroma trajectories are given in the
following (i.e., using the parameters c1
, c2
, cmax
, and p1
, respectively).
Analogous equations apply for the other two coordinates.
The trajectories are functions of the intensity \(i \in [0,1]\) where \(1\) corresponds to the full intensity:
where \(j\) is the intensity at which \(c_{max}\) is assumed. It is constructed such that the slope to the left is the negative of the slope to the right of \(j\):
Instead of using a linear intensity \(i\) going from \(1\) to \(0\), one can replace \(i\) with \(i ^{p_1}\) in the equations above. This then leads to power-transformed curves that add or remove chroma more slowly or more quickly depending on whether the power parameter \(p_1\) is \(< 1\) or \(> 1\).
The three types of trajectories are also depicted below. Note that full intensity \(i = 1\) is on the left and zero intensity \(i = 0\) is on the right of each panel.
(Source code
, png
, hires.png
, pdf
)
The concrete parameters in the plot above are:
Constant:
c1 = 80
.Linear:
c1 = 80
,c2 = 10
,p1 = 1
(solid) vs.p1 = 1.6
(dashed).Triangular:
c1 = 60
,cmax = 80
,c2 = 10
,p1 = 1
(solid) vs.p1 = 1.6
(dashed).
Further discussion of these trajectories and how they can be visualized and assessed for a given color palette is provided in the article Palette Visualization and Assessment.
Registering your own palettes¶
Todo
Registering new palettes not yet implemented.
Flexible diverging palettes¶
The divergingx_hcl
function provides more flexible diverging palettes by
simply calling sequential_hcl
twice with prespecified sets of hue, chroma,
and luminance parameters. Thus, it does not pose any restrictions that the two
“arms” of the palette need to be balanced and also may go through a non-gray
neutral color (typically light yellow). Consequently, the chroma/luminance
paths can be rather unbalanced.
The plot below shows all such flexible diverging palettes that have been named in colorspace:
“ArmyRose” to “Tropic” closely match the palettes of the same name from CARTO [16].
“PuOr” to “Spectral” closely match the palettes of the same name from ColorBrewer.org [3].
“Zissou 1” closely matches the palette of the same name from wesanderson [9]
“Cividis” closely matches the palette of the same name from the viridis family (Garnier 2018). Note that despite having two “arms” with blue vs. yellow colors and a low-chroma center color, this is probably better classified as a sequential palette due to the monotonic chroma going from dark to light. (See Approximating Palettes from Other Packages for more details.)
“Roma” closely matches the palette of the same name by Crameri [2].
(Source code
, png
, hires.png
, pdf
)
Typically, the more restricted diverging_hcl
palettes should be preferred
because they are more balanced. However, by being able to go through light
yellow as the neutral color warmer diverging palettes are available.
References¶
Fabio Crameri. Geodynamic diagnostics, scientific visualisation and \textit StagLab 3.0. Geoscientific Model Development, 11(6):2541–2562, 2018. doi:10.5194/gmd-11-2541-2018.
Mark A. Harrower and Cynthia A. Brewer. \textit ColorBrewer.org: an online tool for selecting color schemes for maps. The Cartographic Journal, 40:27–37, 2003. URL: http://ColorBrewer.org/.
Ross Ihaka. Colour for presentation graphics. In Kurt Hornik, Friedrich Leisch, and Achim Zeileis, editors, Proceedings of the 3rd International Workshop on Distributed Statistical Computing, Vienna, Austria. 2003. ISSN 1609-395X. URL: http://www.ci.tuwien.ac.at/Conferences/DSC-2003/Proceedings/.
Nathaniel Smith and Stéfan Van der Walt. A better default colormap for \textit matplotlib. In SciPy 2015 – Scientific Computing with \textsf Python. Austin, 2015. URL: https://www.youtube.com/watch?v=xAoljeRJ3lU.
Reto Stauffer, Georg J. Mayr, Markus Dabernig, and Achim Zeileis. Somewhere over the rainbow: how to make effective use of colors in meteorological visualizations. Bulletin of the American Meteorological Society, 96(2):203–216, 2015. doi:10.1175/BAMS-D-13-00155.1.
Achim Zeileis, Kurt Hornik, and Paul Murrell. Escaping RGBland: selecting colors for statistical graphics. Computational Statistics & Data Analysis, 53:3259–3270, 2009. doi:10.1016/j.csda.2008.11.033.
CARTO. Cartocolors – data-driven color schemes. 2019. URL: https://carto.com/carto-colors/.