The purpose of the pals
package is twofold:
Memory use is reduced by compressing colormaps to fewer colors and by calling colorRampPalette
only when a colormap is requested.
This report gives some suggestions/recommendations for color and then gives an example of each evaluation tool.
The appearance of color depends on:
It is difficult to give definitive recommendations for the best palettes and colormaps. Nonetheless, here are some we like.
Diverging: coolwarm
/warmcool
avoid Mach banding in the middle.
Sequential (multi-hue): ocean.haline
, parula
(default in Matlab).
Sequential (one hue): brewer.blues
.
Rainbow: cubicl
, kovesi.rainbow
.
Cyclical: ocean.phase
.
Categorical: brewer.paired
, stepped
require(pals)
## Loading required package: pals
pal.bands(coolwarm, parula, ocean.haline, brewer.blues, cubicl, kovesi.rainbow, ocean.phase, brewer.paired(12), stepped,
main="Colormap suggestions")
## Only 24 colors are available with 'stepped'
pals
packageShow palettes and colormaps as colored bands
What to look for:
labs=c('alphabet','alphabet2', 'glasbey','kelly','polychrome', 'stepped', 'tol', 'watlington')
op=par(mar=c(0,5,3,1))
pal.bands(alphabet(), alphabet2(), glasbey(), kelly(),
polychrome(), stepped(), tol(), watlington(), labels=labs, show.names=FALSE)
par(op)
pal.bands(coolwarm, viridis, parula, n=200)
Show the amount of red, green, blue, and gray in colors of a palette. The gray line corresponds to luminosity.
What to look for:
pal.channels(parula, main="parula")
Show a palette with heirarchical clustering
The palette colors are converted to LUV coordinates before clustering.
What to look for:
pal.cluster(alphabet2(), main="alphabet2")
Show a colormap with a Campbell-Robson Contrast Sensitivity Chart.
In a contrast sensitivity figure as drawn by this function, the spatial frequency increases from left to right and the contrast decreases from bottom to top. The bars in the figure appear taller in the middle of the image than at the edges, creating an upside-down “U” shape, which is the “contrast sensitivity function”. Your perception of this curve depends on the viewing distance.
What to look for:
pal.csf(parula, main="parula")
Many colormap functions are defined with more colors than needed. This function compresses a colormap function down to a small-ish vector of colors that can be passed into colorRampPalette
to re-create the original palette with a just-noticeable-difference.
How effective is pal.compress
? Compressing all 50 kovesi.*
colormaps reduces memory from 352000 to 46000 bytes, a savings of 87%.
In the figure below, the top band is the coolwarm
colormap function with 255 colors. The cool2
vector has 13 colors (shown at the bottom) which can be passed into the colorRampPalette
function and expanded to 255 colors shown in the middle band. The maximum squared LUV distance between the individual colors in the two bands is 2.08, which is smaller than the theoretical perceptual difference.
# smooth palettes usually easy to compress
p1 <- coolwarm(255)
cool2 <- pal.compress(coolwarm)
p2 <- colorRampPalette(cool2)(255)
pal.bands(p1, p2, cool2,
labels=c('original','compressed', 'basis'), main="coolwarm")
pal.maxdist(p1,p2) # 2.08
## [1] 2.07927
The palette is converted to RGB or LUV coordinates and plotted in a three-dimensional scatterplot. The LUV space is probably better, but it is easier to tweak colors by hand in RGB space.
What to look for:
#pal.cube(cubehelix)
#pal.cube(polychrome())
A random heatmap is generated (with 5% missing values) and a key is added to the heatmap by appending a blank column along the right side and then a column with the palette colors.
What to look for:
op <- par(mfrow=c(1,2), mar=c(1,1,2,2))
pal.heatmap(alphabet, n=26, main="alphabet")
pal.heatmap(alphabet2, n=26, main="alphabet2")
par(op)
Display a palette on a choropleth map similar to the ColorBrewer website.
What to look for:
pal.map(brewer.paired, n=12, main="brewer.paired")
A single palette/colormap is shown as five colored bands:
What to look for:
pal.safe(parula, main="parula")
Show a colormap with a scatterplot
What to look for:
pal.scatter(polychrome, n=36, main="alphabet")
The test image shows a sine wave superimposed on a ramp of the palette. The amplitude of the sine wave is dampened/modulated from full at the top of the image to 0 at the bottom.
What to look for:
pal.sineramp(parula, main="parula")
In the example below, the jet
colormap fails both tests, the tol.rainbow
colormap fails to clearly show the sinewave in the green/orange region.
op <- par(mfrow=c(3,1), mar=c(1,1,2,1))
pal.sineramp(jet, main="jet")
pal.sineramp(tol.rainbow, main="tol.rainbow")
pal.sineramp(kovesi.rainbow, main="kovesi.rainbow")
par(op)
This function combines several other functions into a single test image.
The examples below show the superiority of the parula
colormap as compared to the viridis
colormap.
What to look for:
The examples below show the poor performance of the ‘viridis’ colormap in dark regions. The ‘parula’ palette shows more structure in the volcano.
pal.test(parula)
pal.test(viridis) # dark colors are poor
Some palettes with dark colors at one end of the palette hide the shape of the volcano in the dark colors.
What to look for:
pal.volcano(parula)
pal.volcano(viridis)
Show a Z-order curve, coloring cells with a colormap. The difference in color between squares side-by-side is 1/48 of the full range. The difference in color between one square atop another is 1/96 the full range.
What to look for:
pal.zcurve(parula, main="parula")
To use any colormap with the ggplot2
package, use the scale_fill_gradientn()
function.
require(ggplot2)
## Loading required package: ggplot2
require(pals)
require(reshape2)
## Loading required package: reshape2
ggplot(melt(volcano), aes(x=Var1, y=Var2, fill=value)) +
geom_tile() +
scale_fill_gradientn(colours=coolwarm(100), guide = "colourbar")
The following images show bands for all the colormaps and palettes in the pals
package, grouped in
# Discrete
pal.bands(alphabet, alphabet2, cols25, glasbey, kelly, polychrome, stepped, tol, watlington,
main="Discrete", show.names=FALSE)
## Only 26 colors are available with 'alphabet'
## Only 26 colors are available with 'alphabet2'
## Only 25 colors are available with 'cols25'.
## Only 32 colors are available with 'glasbey'.
## Only 22 colors are available with 'kelly'.
## Only 36 colors are available with 'polychrome'.
## Only 24 colors are available with 'stepped'
## Only 12 colors are available with 'tol'
## Only 16 colors are available with 'watlington'.
# Misc
pal.bands(coolwarm,warmcool,cubehelix,gnuplot,jet,parula,tol.rainbow)
# Niccoli
pal.bands(cubicyf,cubicl,isol,linearl,linearlhot,
main="Niccoli")
# Qualtitative
pal.bands(brewer.accent(8), brewer.dark2(8), brewer.paired(12), brewer.pastel1(9),
brewer.pastel2(8), brewer.set1(9), brewer.set2(8), brewer.set3(10),
labels=c("brewer.accent", "brewer.dark2", "brewer.paired", "brewer.pastel1",
"brewer.pastel2", "brewer.set1", "brewer.set2", "brewer.set3"),
main="Brewer qualitative")
# Sequential
pal.bands(brewer.blues, brewer.bugn, brewer.bupu, brewer.gnbu, brewer.greens,
brewer.greys, brewer.oranges, brewer.orrd, brewer.pubu, brewer.pubugn,
brewer.purd, brewer.purples, brewer.rdpu, brewer.reds, brewer.ylgn,
brewer.ylgnbu, brewer.ylorbr, brewer.ylorrd,
main="Brewer sequential")
# Diverging
pal.bands(brewer.brbg, brewer.piyg, brewer.prgn, brewer.puor, brewer.rdbu,
brewer.rdgy, brewer.rdylbu, brewer.rdylgn, brewer.spectral,
main="Brewer diverging")
# Ocean
pal.bands(ocean.thermal, ocean.haline, ocean.solar, ocean.ice, ocean.gray,
ocean.oxy, ocean.deep, ocean.dense, ocean.algae, ocean.matter,
ocean.turbid, ocean.speed, ocean.amp, ocean.tempo, ocean.phase,
ocean.balance, ocean.delta, ocean.curl, main="Ocean")
# Matplotlib
pal.bands(magma, inferno, plasma, viridis, main="Matplotlib")
# Kovesi
op = par(mar=c(1,10,2,1))
pal.bands(kovesi.cyclic_grey_15_85_c0, kovesi.cyclic_grey_15_85_c0_s25,
kovesi.cyclic_mrybm_35_75_c68, kovesi.cyclic_mrybm_35_75_c68_s25,
kovesi.cyclic_mygbm_30_95_c78, kovesi.cyclic_mygbm_30_95_c78_s25,
kovesi.cyclic_wrwbw_40_90_c42, kovesi.cyclic_wrwbw_40_90_c42_s25,
kovesi.diverging_isoluminant_cjm_75_c23, kovesi.diverging_isoluminant_cjm_75_c24,
kovesi.diverging_isoluminant_cjo_70_c25, kovesi.diverging_linear_bjr_30_55_c53,
kovesi.diverging_linear_bjy_30_90_c45, kovesi.diverging_rainbow_bgymr_45_85_c67,
kovesi.diverging_bkr_55_10_c35, kovesi.diverging_bky_60_10_c30,
kovesi.diverging_bwr_40_95_c42, kovesi.diverging_bwr_55_98_c37,
kovesi.diverging_cwm_80_100_c22, kovesi.diverging_gkr_60_10_c40,
kovesi.diverging_gwr_55_95_c38, kovesi.diverging_gwv_55_95_c39,
kovesi.isoluminant_cgo_70_c39, kovesi.isoluminant_cgo_80_c38,
kovesi.isoluminant_cm_70_c39, kovesi.rainbow_bgyr_35_85_c72, kovesi.rainbow_bgyr_35_85_c73,
kovesi.rainbow_bgyrm_35_85_c69, kovesi.rainbow_bgyrm_35_85_c71,
main="Kovesi")
pal.bands(kovesi.linear_bgy_10_95_c74,
kovesi.linear_bgyw_15_100_c67, kovesi.linear_bgyw_15_100_c68,
kovesi.linear_blue_5_95_c73, kovesi.linear_blue_95_50_c20,
kovesi.linear_bmw_5_95_c86, kovesi.linear_bmw_5_95_c89,
kovesi.linear_bmy_10_95_c71, kovesi.linear_bmy_10_95_c78,
kovesi.linear_gow_60_85_c27, kovesi.linear_gow_65_90_c35,
kovesi.linear_green_5_95_c69, kovesi.linear_grey_0_100_c0,
kovesi.linear_grey_10_95_c0, kovesi.linear_kry_5_95_c72,
kovesi.linear_kry_5_98_c75, kovesi.linear_kryw_5_100_c64,
kovesi.linear_kryw_5_100_c67, kovesi.linear_ternary_blue_0_44_c57,
kovesi.linear_ternary_green_0_46_c42, kovesi.linear_ternary_red_0_50_c52,
main="Kovesi linear"
)
par(op)