Palette Visualization and Assessment¶
Overview¶
Create color palette swatch plots. |
|
Visualization of the RGB and HCL spectrum given a set of hex colors. |
|
Create demo plots. |
Color swatches¶
The function swatchplot()
is a convenience function for displaying
collections of palettes that can be specified in different ways, e.g.,
an object of class palette
, colorlib.colorobject
, or
plaettes.hclpalette
with a single palette or a list or dictionary to display
and group multiple palettes at once.
The arguments to control the swatches might be updated in a future version to allow for a bit more flexibility.
(Source code
, png
, hires.png
, pdf
)
This shows the following:
Hue: Only the hue (= type of color) changes from
H = 0
(red) via60
(yellow), etc. to300
(purple) while chroma and luminance are fixed to moderate values ofC = 60
andL = 65
, respectively.Chroma: Only the chroma (= colorfulness) changes from
C = 0
(gray) to100
(colorful) while hue and luminance are fixed toH = 0
(red) andL = 65
, respectively.Luminance: Only the luminance (= brightness) changes from
L = 90
(light) to25
(dark) while hue and chroma are fixed toH = 260
(blue) andC = 25
(low, close to gray), respectively.
Next, we demonstrate a more complex example of a
swatchplot()
call where the object provided is
a dictionary of lists where each list contains a series of 4
palettes.hclpalette
object consisting of blues, purples, reds, and greens.
The keys of the dictionary are used for ‘group titles’, the name of the
palettes is suppressed in this case (argument show_names = False
).
For all palettes, luminance increases monotonically to yield a proper sequential palette. However, the hue and chroma handling is somewhat different to emphasize different parts of the palette.
Single-hue: In each palette the hue is fixed and chroma decreases monotonically (along with increasing luminance). This is typically sufficient to clearly bring out the extreme colors (dark/colorful vs. light gray).
Single-hue (advanced): The hue is fixed (as above) but the chroma trajectory is triangular. Compared to the basic single-hue palette above, this better distinguishes the colors in the middle and not only the extremes.
Multi-hue (advanced): As in the advanced single-hue palette, the chroma trajectory is triangular but additionally the hue varies slightly. This can further enhance the distinction of colors in the middle of the palette.
In [1]: from colorspace import swatchplot, sequential_hcl
In [2]: swatchplot({"Single-hue": [sequential_hcl(x) for x in ["Blues 2", "Purples 2", "Reds 2", "Greens 2"]],
...: "Single-hue (advanced)": [sequential_hcl(x) for x in ["Blues 3", "Purples 3", "Reds 3", "Greens 3"]],
...: "Multi-hue (advanced)": [sequential_hcl(x) for x in ["Blues", "Purples", "Reds", "Greens"]]},
...: n = 7, show_names = False, nrow = 5, figsize = (12, 3))
...:
Out[2]: <Figure size 1200x300 with 1 Axes>
Moreover, the cvd
argument can be set to a vector of transformation names,
indicating which deficiencies to emulate. In the example below this is used to
compare the Viridis and YlGnBu palettes under deuteranopia and desaturation.
In [3]: from colorspace import sequential_hcl, palette, swatchplot
In [4]: pal1 = sequential_hcl("YlGnBu")(7)
In [5]: pal2 = sequential_hcl("Viridis")(7)
In [6]: swatchplot([palette(pal1, "YlGnBu"), palette(pal2, "Viridis")],
...: cvd = ["protan", "desaturate"], nrow = 4, figsize = (8, 2.5))
...:
Out[6]: <Figure size 800x250 with 1 Axes>
HCL (and RGB) spectrum¶
As the properties of a palette in terms of the perceptual dimensions hue,
chroma, and luminance are not always clear from looking just at color swatches
or (statistical) graphics based on these palettes, the specplot()
function provides an explicit display for the coordinates of the HCL trajectory
associated with a palette. This can bring out clearly various aspects, e.g.,
whether hue is constant, whether chroma is monotonic or triangular, and whether
luminance is approximately constant (as in many qualitative palettes),
monotonic (as in sequential palettes), or diverging.
The function first transforms a given color palette to its HCL (polarLUV)
coordinates. As the hues for low-chroma colors are not (or only poorly)
identified, they are smoothed by default. Also, to avoid jumps from 0
to
360
or vice versa, the hue coordinates are shifted suitably.
By default, the resulting trajectories in the HCL spectrum are visualized by a simple line plot:
Hue is drawn in red and coordinates are indicated on the axis on the right with range
[-360, 360]
.Chroma is drawn in green with coordinates on the left axis. The range
[0, 100]
is used unless the palette necessitates higher chroma values.Luminance is drawn in blue with coordinates on the left axis in the range
[0, 100]
.
Additionally, a color swatch for the palette is included. Optionally, a second spectrum for the corresponding trajectories of RGB coordinates can be included. However, this is usually just of interest for palettes created in RGB space (or simple transformations of RGB).
The illustrations below show how basic qualitative, sequential, and diverging
palettes are constructed in HCL space (the corresponding mathematical equations
are provided in the Construction details).
In the qualitative "Set 2"
palette below, the hue traverses the entire color
“wheel” (from 0
to 360
degrees) while keeping chroma and luminance (almost)
constant (C = 60
and L = 70
).
In [7]: from colorspace import specplot, qualitative_hcl
In [8]: specplot(qualitative_hcl("Set 2").colors(100))
Out[8]: <Figure size 640x480 with 3 Axes>
Note that due to the restrictions of the HCL color space, some of the green/blue colors have a slightly smaller maximum chroma resulting in a small dip in the chroma curve. This is fixed automatically (by default) and is hardly noticable in visualizations, though.
The sequential “Blues 2” palette below employs a single hue (H = 260
) and a
monotonically increasing luminance trajectory (from dark to light). Chroma
simply decreases monotonically along with increasing luminance.
In [9]: from colorspace import specplot, sequential_hcl
In [10]: specplot(sequential_hcl("Blues 2").colors(100))
Out[10]: <Figure size 640x480 with 3 Axes>
Finally, the diverging “Blue-Red” palette is depicted below. It simply combines a blue and a red sequential single-hue palette (similar to the “Blues 2” palette discussed above). Hue is constant in each “arm” of the palette and the chroma/luminance trajectories are balanced between both arms. In the center the palette passes through a light gray (with zero chroma) as the neutral value.
In [11]: from colorspace import specplot, diverging_hcl
In [12]: specplot(diverging_hcl("Blue-Red").colors(100))
Out[12]: <Figure size 640x480 with 3 Axes>
To contrast these well-balanced HCL-based palettes with a poorly-balanced palette, the spectrum of the (in)famous RGB rainbow palette is depicted in both RGB and HCL space.
In [13]: from colorspace import specplot, rainbow
In [14]: specplot(rainbow().colors(100))
Out[14]: <Figure size 640x480 with 3 Axes>
Trajectories in HCL space¶
Todo
Section must be written.
Demonstration of statistical graphics¶
To demonstrate how different kinds of color palettes work in different kinds of
statistical displays, demoplot() provides a simple convenience interface to
some base graphics with (mostly artificial) data sets. As a first overview, all
built-in demos are displayed with the same sequential heat colors palette:
sequential_hcl("Heat")(7)
.
In [15]: from matplotlib import pyplot as plt
In [16]: from colorspace import demoplot, sequential_hcl
In [17]: colors = sequential_hcl("Heat").colors(5)
In [18]: fig, axes = plt.subplots(2, 4, figsize = (10, 6))
In [19]: demo_types = ["Bar", "Heatmap", "Lines", "Matrix", "Pie", "Spine", "Map", None]
In [20]: for i in range(0, len(demo_types)):
....: if demo_types[i] == None:
....: fig.delaxes(axes.flatten()[i])
....: else:
....: demoplot(colors, type_ = demo_types[i], title = demo_types[i], ax = axes.flatten()[i])
....:
In [21]: fig.show()
All types of demos can, in principle, deal with arbitrarily many colors from any palette, but the graphics differ in various respects such as:
Working best for fewer colors (e.g., bar, pie, lines, …) vs. many colors (e.g., heatmap, …).
Intended for categorical data (e.g., bar, pie, …) vs. continuous numeric data (e.g., heatmap, …).
Shading areas (e.g., map, bar, pie, …) vs. coloring points or lines.
Hence, in the following some further illustrations are organized by type of palette, using suitable demos for the particular palettes.
Qualitative palettes: Light pastel colors typically work better for shading areas (pie, left) while darker and more colorful palettes are usually preferred for lines (right).
In [22]: from matplotlib import pyplot as plt
In [23]: from colorspace import demoplot, qualitative_hcl
In [24]: fig, [ax1, ax2] = plt.subplots(1, 2, figsize = (6.5, 3))
In [25]: ax1 = demoplot(qualitative_hcl("Pastel 1"), type_ = "Pie", n = 4, ax = ax1)
In [26]: ax2 = demoplot(qualitative_hcl("Dark 3"), type_ = "Lines", n = 4, ax = ax2)
In [27]: fig.show()
Sequential palettes: Heatmaps (left) often employ almost continuous gradients with strong luminance contrasts. In contrast, when only a few ordered categories are to be displayed (e.g., in a spine plot, right) more colorful sequential palettes like the viridis palette can be useful.
In [28]: from matplotlib import pyplot as plt
In [29]: from colorspace import demoplot, sequential_hcl
In [30]: fig, [ax1, ax2] = plt.subplots(1, 2, figsize = (6.5, 3))
In [31]: ax1 = demoplot(sequential_hcl("Purple-Blue", rev = True), type_ = "Heatmap", n = 99, ax = ax1)
In [32]: ax2 = demoplot(sequential_hcl("Viridis"), type_ = "Spine", n = 4, ax = ax2)
In [33]: fig.show()
Diverging palettes: In some displays (such as the map, left), it is useful to employ an almost continuous gradient with strong luminance contrast to bring out the extremes. Here, this contrast is amplified by a larger power transformation emphasizing the extremes even further. In contrast, when fewer colors are needed more colorful palettes with lower luminance contrasts can be desired. This is exemplified by a mosaic (center) and bar plot (right).
In [34]: from matplotlib import pyplot as plt
In [35]: from colorspace import demoplot, sequential_hcl
In [36]: fig, [ax1, ax2, ax3] = plt.subplots(1, 3, figsize = (10, 3))
In [37]: ax1 = demoplot(diverging_hcl("Tropic", power = 2.5), type_ = "Map", n = 99, ax = ax1)
In [38]: ax2 = demoplot(diverging_hcl("Green-Orange"), type_ = "Matrix", n = 5, ax = ax2)
In [39]: ax3 = demoplot(diverging_hcl("Blue-Red 2"), type_ = "Bar", n = 5, ax = ax3)
In [40]: fig.show()
All displays above focus on palettes designed for light/white backgrounds. Therefore, to conclude, some palettes are highlighted that work well on dark/black backgrounds.
In [41]: from matplotlib import pyplot as plt
In [42]: from colorspace import demoplot, sequential_hcl, qualitative_hcl, diverging_hcl
In [43]: fig, axes = plt.subplots(2, 3, figsize = (10, 6))
In [44]: plt.style.use("dark_background")
In [45]: axes[0, 0] = demoplot(sequential_hcl("Oslo", rev = True), "Heatmap", ax = axes[0, 0])
In [46]: axes[0, 1] = demoplot(sequential_hcl("Turku", rev = True), "Heatmap", ax = axes[0, 1])
In [47]: axes[0, 2] = demoplot(sequential_hcl("Inferno"), "Heatmap", ax = axes[0, 2])
In [48]: axes[1, 0] = demoplot(qualitative_hcl("Set 2"), "Lines", ax = axes[1, 0])
In [49]: axes[1, 1] = demoplot(diverging_hcl("Berlin"), "Lines", ax = axes[1, 1])
In [50]: axes[1, 2] = demoplot(diverging_hcl("Cyan-Magenta", l2 = 20), "Lines", ax = axes[1, 2])
In [51]: fig.show()
In [52]: import matplotlib as mpl
In [53]: mpl.rcParams.update(mpl.rcParamsDefault)