colorjam
provides visually distinct categorical colors of arbitrary length, using an optimized pattern of chroma/luminance values.
Scalable. Visually distinct categorical colors of arbitrary length.
rainbowJam(n)
scale_color_jam()
, scale_fill_jam()
Color-blindness friendly. Optimized for three kinds of color blindness.
"dichromat"
, "ryb"
, "rgb"
color wheels are available.launchColorjamShiny()
Install colorjam using the remotes
package:
remotes::install_github("jmw86069/colorjam");
OR, use pacman
to keep the package updated:
### if necessary, install pacman:
# install.packages("pacman")
library(pacman)
p_load_current_gh("jmw86069/colorjam")
The colorjam
package is being prepared for CRAN in the “near” future.
For the examples below, two packages are loaded:
Let’s generate n=5
categorical colors, displayed by jamba::showColors()
.
showColors(rainbowJam(5));
Alternatively, color_pie()
displays colors in a pie circle.
color_pie(rainbowJam(5));
Categorical colors are scalable.
color_pie(rainbowJam(15));
Label the colors using the 4994 named_colors
:
color_pie(rainbowJam(15, nameStyle="closest_named_color"));
Gradually increase the number of colors, then use color_pie()
to plot them in concentric circles.
colorList <- lapply(nameVector(c(36, 24, 12)), function(n){
rainbowJam(n, nameStyle="n");
});
color_pie(colorList,
main="preset='dichromat2' (default)");
Every color system has a “color wheel” - something like red-green-blue (RGB) or red-yellow-blue (RYB).
We defined a new color wheel "dichromat"
to maximize the visual distinction between color hues for people with color blindness. The process was driven by R package dichromat
, so we gave it that name out of respect.
The "dichromat"
color wheel allocates approximately equal halves of the color wheel to be visually distinct for "deutan"
, "protan"
, and "tritan"
forms of color blindness. Roughly akin to using “cool”/“warm” colors for each half the color wheel, for each simulated color. The wheel avoids colors which are the most difficult to distinguish in the color wheel.
It isn’t perfect.
However colorjam
does provide the first scalable method (we have seen) to produce categorical colors optimized for the three major forms of color-blindness. Other excellent resources that provide color-blindness friendly colors, which are not scalable. However to be fair, fixed colors may be the best realistic approach, so colorjam
may not be the ideal solution.
The “full rainbow” color wheel “red-yellow-blue” is recommended over default RGB to provide the best full rainbow. It performs particularly well for color blending (see Color-blending) for additive paint-like mixing.
preset="ryb"
for red-yellow-blue
colorList1 <- lapply(nameVector(c(12)), function(n){
rainbowJam(n, preset="ryb");
});
color_pie(colorList1,
main="Red-Yellow-Blue\npreset='ryb' (starting at red)");
preset="ryb2"
(recommended) for yellow-red-blue, starting with yellow
colorList1 <- lapply(nameVector(c(16)), function(n){
rainbowJam(n, preset="ryb2");
});
color_pie(colorList1,
main="Yellow-Red-Blue\npreset='ryb2' (starting at yellow)");
The reason to start with yellow is noticed when the first category in a set is the reference or control group in a scientific experiment. It is intuitive for the reference/control to have a neutral color, instead of being bright red.
Previous versions (<= 0.0.23.900) of colorjam used a red-yellow-blue color wheel starting with red.
Similarly, the R default “red-green-blue” color wheel:
preset="rgb"
the R default RGB color wheel
color_pie(
rainbowJam(16, preset="rgb"),
main="Red-Green-Blue\npreset='rgb' (starting at red)");
(Look how much of this color wheel is blue-green. This style is not for me, haha.)
preset="rgb2"
the R default RGB color wheel, starting with yellow
color_pie(
rainbowJam(16, preset="rgb2"),
main="Red-Green-Blue\npreset='rgb2' (starting at yellow)");
The 4994 colors provided in named_colors
(see next section) are collated from numerous sources, and ultimately represent colors that people were motivated to name. Look how many named colors include red/orange/yellow as compared to green/blue/purple! Then compare to the RGB color wheel above, which disproportionately represents blue/green, to the detriment of red/yellow.
Here, named_colors
are filtered for at least Chroma 40 using subset_colors(named_colors, C > 40)
color_pie(unname(
subset_colors(named_colors, C > 40)))
Clearly people can see many more red-orange-yellow, and comparatively very few green/blue colors. This bias is partly from lower sensitivity of colors such as “cyan”, and partly due to RGB color monitors being unable to produce high saturation colors with that hue. Color theory is fascinating, and endlessly complex, in part because each person is slightly different.
Two functions are provided to match colors to a reference set, which is a convenient way to assign color names.
grDevices::colors()
, custom reference colors can be supplied.named_colors
, which adds 4447 colors from meodai/named-colors (amazing!) and 436 colors not already represented from grDevices::colors()
.The argument showPalette=TRUE
will plot the original colors and the closest matched color for comparison.
cnc <- closest_named_color(c(rainbowJam(12), "grey"),
showPalette=TRUE,
main="closest_named_color() using `named_colors`");
crc <- closestRcolor(c(rainbowJam(12), "grey"),
showPalette=TRUE,
main="closestRcolor() using `colors()`");
There are two underlying methods:
"HCL"
(default) matches color hue by angle, with custom weights to channels H, C, and L."LUV"
matches colors using non-polar coordinates and Euclidean distance across the channels L, U, and V.Greyscale colors are matches separately to a subset of grayscale reference colors, to avoid using hue in unsaturated colors.
blend_colors()
has some useful features:
The argument do_plot=TRUE
will plot a visual summary of the mixing results.
blent1 <- blend_colors(c("red", "blue"), do_plot=TRUE);
blent2 <- blend_colors(c("gold", "blue"), do_plot=TRUE);
blent3 <- blend_colors(c("gold", "red"), do_plot=TRUE);
blent8 <- blend_colors(c("red1", "red3", "blue"), do_plot=TRUE);
blent9 <- blend_colors(c("red1", "blue1", "blue4"), do_plot=TRUE);
blent10 <- blend_colors(c("red", "blue", "ivory"), do_plot=TRUE);
color2gradient()
can split colors using a light-dark gradient.
colorSet <- rainbowJam(5);
colorSet4 <- color2gradient(colorSet, n=4);
color_pie(list(
colorSet4=unname(colorSet4),
colorSet=rep(colorSet, each=4)),
main="Color split into 4 additional subsets.");
The intensity of the gradient is adjusted with dex
, “darkness expansion factor”.
colorSet <- rainbowJam(5);
colorSet4a <- color2gradient(colorSet,
n=4,
dex=1/2);
colorSet4c <- color2gradient(colorSet,
n=4,
dex=3);
colorSet4b <- color2gradient(colorSet,
n=4,
dex=10);
colorSet <- rep(colorSet, each=4)
names(colorSet4c) <- names(colorSet4b) <- names(colorSet4a) <- names(colorSet4) <- "";
names(colorSet4b)[5:8] <- c(" 10", " |", " |", " v")
names(colorSet4c)[5:8] <- c(" 3", " |", " |", " v")
names(colorSet4)[5:8] <- c(" 1", " |", " |", " v")
names(colorSet4a)[5:8] <- c("1/2", " |", " |", " v")
color_pie(list(
`dex=10`=(colorSet4b),
`dex=3`=(colorSet4c),
`dex=1\n(default)`=(colorSet4),
`dex=1/2`=(colorSet4a),
colorSet=colorSet),
main=paste0("Intensity of the gradient is adjusted with 'dex'\n",
"(darkness expansion factor)"));
scale_color_jam()
categorical colors for ggplot2 colour
scale_fill_jam()
categorical colors for ggplot2 fill
if (suppressPackageStartupMessages(require(ggplot2))) {
dsamp <- ggplot2::diamonds[sample(nrow(ggplot2::diamonds), 1000),];
d <- ggplot2::ggplot(dsamp, ggplot2::aes(carat, price)) +
ggplot2::geom_point(ggplot2::aes(colour=cut, fill=cut), size=4, shape=21);
d +
scale_color_jam() +
scale_fill_jam() +
ggplot2::ggtitle("scale_color_jam()");
}
Colors can be adjusted for darkness, saturation, to make interesting point shapes:
if (suppressPackageStartupMessages(require(ggplot2))) {
d +
scale_color_jam(darkFactor=1.5) +
scale_fill_jam(darkFactor=-1.2) +
ggplot2::ggtitle("Adjustment using 'darkFactor'");
}
An alternative ggplot2 theme is provided.
if (suppressPackageStartupMessages(require(ggplot2))) {
d +
scale_color_jam(darkFactor=1.5) +
scale_fill_jam(darkFactor=-1.2) +
ggplot2::ggtitle("theme_jam()") +
theme_jam()
}
This function provides some common arguments to customize:
base_size
: numeric
default font size in points.blankGrid
: logical
to remove all background grid lines.
if (suppressPackageStartupMessages(require(ggplot2))) {
d +
scale_color_jam(darkFactor=1.5) +
scale_fill_jam(darkFactor=-1.2) +
ggplot2::ggtitle("theme_jam()") +
theme_jam(base_size=24)
}
jam_linear
: linear (sequential) gradients with white baseline color
jamba::showColors(jam_linear)
jam_divergent
: divergent color gradients with black baseline color
jamba::showColors(jam_divergent)
The driving use case was to display genome sequence coverage heatmaps with slightly different colors for each type of signal. We wanted linear and divergent color gradients to use in tandem, for example "jam_linear$firebrick"
and "jam_divergent$firebrick_skyblue"
.
twostep_gradient()
combines two linear gradients into one linear gradient.
Two linear gradients are combined, using orange and red:
ts1 <- twostep_gradient("orange2", "firebrick", n=11, debug=TRUE)
#> w1 w2
#> 1 1.000 0.000
#> 2 1.000 0.000
#> 3 0.838 0.162
#> 4 0.686 0.314
#> 5 0.544 0.456
#> 6 0.414 0.586
#> 7 0.296 0.704
#> 8 0.192 0.808
#> 9 0.105 0.895
#> 10 0.037 0.963
#> 11 0.000 1.000
title("orange2 + firebrick");
Two linear gradients are combined, using aquamarine and blue:
ts2 <- twostep_gradient("aquamarine", "dodgerblue", n=11, debug=TRUE)
#> w1 w2
#> 1 1.000 0.000
#> 2 1.000 0.000
#> 3 0.838 0.162
#> 4 0.686 0.314
#> 5 0.544 0.456
#> 6 0.414 0.586
#> 7 0.296 0.704
#> 8 0.192 0.808
#> 9 0.105 0.895
#> 10 0.037 0.963
#> 11 0.000 1.000
title("aquamarine + dodgerblue");
make_jam_divergent()
combines two linear gradients.
ts1ts2 <- make_jam_divergent(list(ts2=ts2),
list(ts1=ts1),
n=21)
jamba::showColors(ts1ts2)