The retina of the eye has two kinds of receptors
Images from Ware (2008)
This effect is due to the low sensitivity of cones to blue wavelengths
Yellow wavelengths excite two different types of cones, making it almost as light as pure white.
The brain combines signals from different cones to build three channels:
The effect of contrast is distortion of the appearance of a patch of color in a way that increases the difference between a color and its surroundings.
We talk about luminance contrast when it occurs on the black-white channel, and chromatic when it occurs on the other two channels
This phenomenon is called simultaneous contrast, where the background interpheres with our perception of a patch of color. It can create problems when reading values from a graphic.
When there is a strong positive or negative signal on one of the three channels, and a neutral one on the other two, we have “special” colors
In most languages, these six colors are identified as the basic ones [Brent Berlin and Paul Kay, 1969. Basic Color Terms: Their Universality and Evolution]
A considerable number of people is missing one or more color channels. Most commonly, the missing channel is the red-green one.
As we shall see, when designing a color scale we need to take this into account in order to be inclusive.
Colors inducing a strong response on the chromatic channels are more “vivid”, and are said to be more saturated.
The maximum saturation for a given hue varies with luminance, because when colors are dark the difference between cone signals on chromatic channels is smaller.
Remember the discriminability issue? Here is it at play!
The luminance channel is more effective at conveying spatial details.
We perceive three dimensional surfaces through changes of luminance, rather than through chromatic changes.
To work with colors, we need to agree on a representation. Such representations of colors are called color spaces
\((red, green, blue)\)
In computer representations, each component goes from 0 to 255
Why red, green and blue? This is the set of colors with the widest gamut, that is the set of all colors that can be defined by means of combining the three primary colors.
( 235, 91, 52 ) xxxx #eb5b34
RGB is computationally convenient, but is a poor fit for how our eyes work: it is not perceptually accurate.
In a perceptually uniform color space, colors with the same perceptual distance are at the same distance in the space.
What we intuitively think of as pure colors
The “colorfulness” or intensity of the color. From “vivid” to “muted”
Intuitively, the brightness of the color, or the amount of black mixed into the color. From “dark” to “light”
Source: https://socviz.co/lookatdata.html#perception-and-data-visualization
Source: https://socviz.co/lookatdata.html#perception-and-data-visualization
Because of the effects above, it is best not to encode more than 3 to 5 levels using the Chroma or Luminance channel, if we want our readers to be able to distinguish the levels (discriminability)
A colormap specifies a mapping between colors and data values
There are mainly two things to pay attention to
We can distinguish just about 12 bins of color, better to stick to at most 6
Luminance contrast: we need our colored marks to “stand out” from the background.
Source: Munzner, ch.10, fig 10.8.
It’s often used to encode ordered data, why is it confusing?
You don’t need to create your colormaps from scratch. R sports a rich collection of colormaps ready for use
Install some additional libraries
Call the wizard!
The wizard shows, among other things, the specplot
which reports how the HCL values change in the palette
The contrast ratio, as defined by the World Wide Web Consortium, is a number quantifying the contrast with the background. It should be higher than 4 for text, as a general guideline
Can be used to encode two different variables with color: use with extreme care!
pal1 <- tibble(c1 = c("#f5f5f5","#EE744B","#9F1401"),
dim1 = c("low", "mid", "high"))
pal2 <- tibble(c2 = c("#f5f5f5","#878FD3","#07489C"),
dim2 = c("low", "mid", "high"))
crossing(pal1, pal2) %>%
mutate(
color = hex(mixcolor(0.5, hex2RGB(c1), hex2RGB(c2))),
across(starts_with("dim"), ~ factor(.x,
levels = c("low", "mid", "high"),
ordered=T))
) %>%
ggplot(aes(x=dim1, y=dim2, fill=color)) +
geom_tile() +
scale_fill_identity() +
theme_classic() +
theme(axis.line = element_blank())
Source: Timo Grossenbacher
bg_color <- "gray5"
mortality <-
read_csv('death_rates_ITA.csv') |>
#read_csv('Topics/Color/death_rates_ITA.csv') |>
mutate(Percentile = ntile(Mortality, 100))
ggplot(mortality, aes(Year, Age, fill=Percentile)) +
geom_tile() +
scale_fill_viridis_c(
option='magma',
direction=-1,
breaks = seq(20, 100, by=20)
) +
scale_x_continuous( breaks=seq(1850, 2010, by=20) ) +
scale_y_continuous( breaks=seq(0, 100, by=10) ) +
facet_wrap(vars(Sex), ncol=1) +
labs(
caption = "Source: mortality.org"
) +
theme_void() +
theme(
text = element_text(color = "gray90"),
strip.text = element_text(hjust=0),
axis.text.x = element_text(),
axis.text.y = element_text(),
panel.background = element_rect(fill = bg_color),
plot.background = element_rect(fill = bg_color),
legend.position = "top",
legend.key.width = unit(0.1, "npc")
)