
---
title: "Overview of the 'pals' package"
author: "Kevin Wright"
date: "`r Sys.Date()`"
bibliography: pals.bib
output:
  html_document:
    highlight: tango
vignette: >
  % \VignetteIndexEntry{Overview of the 'pals' package}
  % \VignetteEncoding{UTF-8}
  % \VignetteEngine{knitr::rmarkdown}
---
<style type="text/css">
body, td { font-size: 14pt; color: #000; padding:1em; }
code, pre, #ws, #message { font-size: 12pt;
  color: #000; background-color: #f5f5f5; }
code {border: 1px solid #e0e0e0; padding: 0 5px;}
pre > code { border: 0; }
</style>

# Abstract

The purpose of the `pals` package is twofold: (1) provide a comprehensive collection of color palettes and colormaps (2) provide tools for evaluating these collections.  This report gives some suggestions/recommendations for color and then gives an example of each evaluation tool.

```{r setup, echo=FALSE, results="hide"}
library("knitr")
opts_chunk$set(fig.align="center", fig.width=6, fig.height=6)
options(width=90)
```
# Recommendations

The appearance of color depends on (1) the display device (screen, paper, photocopy), (2) the type of graphic (regions/lines), and (3) the person (age, gender, colorblindness)--it is difficult to give definitive recommendations for the best palettes and colormaps.  Nonetheless, here are some we like (among many!).

Diverging: `coolwarm`/`warmcool` avoid Mach banding in the middle.

Sequential: `ocean.haline`, `parula` (default in Matlab).

Rainbow: `cubicl`, `kovesi.rainbow`.

Cyclical: `ocean.phase`.

Categorical: `brewer.paired`, `stepped`

```{r}
require(pals)
pal.bands(coolwarm, parula, ocean.haline, cubicl, kovesi.rainbow, ocean.phase, brewer.paired(12), stepped,
          main="Colormap suggestions")
```

# Functions in the `pals` package

## pal.bands()

Show palettes and colormaps as colored bands

What to look for:

1. A good discrete palette has distinct colors.

2. A good continuous colormap does not show boundaries between colors.
The \code{rainbow()} palette is poor, showing bright lines at yellow, cyan, pink.

```{r}
labs=c('alphabet','alphabet2', 'glasbey','kelly','pal36', 'stepped', 'tol', 'watlington')
op=par(mar=c(0,5,3,1))
pal.bands(alphabet(), alphabet2(), glasbey(), kelly(),
  pal36(), stepped(), tol(), watlington(), labels=labs, show.names=FALSE)
par(op)
pal.bands(coolwarm, viridis, parula, n=200)
```

## pal.channels()

Show the amount of red, green, blue, and gray in colors of a palette.  The gray line corresponds to luminosity.

What to look for:

1. Sequential data should usually be shown with a colormap that is smoothly
increasing in lightness, as shown by the gray line.
```{r}
pal.channels(parula, main="parula")
```

## pal.cluster()

Show a palette with heirarchical clustering

The palette colors are converted to LUV coordinates before clustering.

What to look for:

1. Colors that are visually similar tend to be clustered together.

2. Are the leaves at substantially different heights?

```{r}
pal.cluster(alphabet2(), main="alphabet2")
```

## pal.csf()

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:

1. Are the vertical bands visible across the full vertical axis?

2. Do the vertical bands blur together?
 

```{r}
pal.csf(parula, main="parula")
```

## pal.compress()

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.
```{r}
# 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

```

## pal.cube()

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:

A good palette has colors that are spread somewhat uniformly in 3D.

```{r}
#pal.cube(cubehelix)
#pal.cube(pal36())
```

![cubehelix](cubehelix.png)
![pal36](pal36.png)

## pal.heatmap()

A random heatmap is generated (with 5% missing values) and a key is added
to the heatmap by appending a blank column and then a column with the
palette colors.

What to look for:

1. Can the value of each cell be correctly interpreted using the key on 
the right side?

2. Can missing values be identified?

```{r}
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)
```

## pal.map()

Display a palette on a choropleth map similar to the ColorBrewer website.

What to look for:

1. Are regions distinct from each other? Are county outlines visible?

2. Are outliers identifiable within each region?

3. Are colors identifiable in the complex area (lower left) part of the map.

```{r}
pal.map(brewer.paired, n=12, main="brewer.paired")
```

## pal.safe()

A single palette/colormap is shown as colored bands
(1) without any modifications 
(2) in black-and-white as if photocopied 
(3) as seen by deutan color-blind
(4) as seen by protan color-blind
(5) as seen by tritan color-blind.

What to look for:

1. Are colors still unique when viewed in less-than full color? 

2. Is a sequential colormap still sequential?

```{r}
pal.safe(parula, main="parula")
```

## pal.scatter()

Show a colormap with a scatterplot

What to look for:

1. Can the colors of each point be uniquely identified?

```{r}
pal.scatter(pal36, n=36, main="alphabet")
```

## pal.sineramp()

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:

1. Is the sine wave equally visible horizontally across the entire image?

2. At the bottom, is the ramp smooth, or are there features like vertical bands?

```{r}
pal.sineramp(parula, main="parula")
```

In the example below, the `jet` palette fails the tests.
```{r}
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)
```

## pal.test()

The `pal.test` 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:

1. Are all squares in the upper-left Z-curve distinct?
2. Are the CSF bands visible?
3. Is the sinewave showing bands of similar height, and smoothly varying along the bottom?
4. Is the actual peak of the volcano identifiable? The lowest area?
5. Is the gray/luminosity channel monotonic?

```{r}
pal.test(parula)
pal.test(viridis) # dark colors are poor
```

## pal.volcano()

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:

1. Can you locate the exact position of the highest point on the volcano?

2. Are the upper-right and lower-right corners the same elevation?

3. Do any Mach bands circle the peak?

```{r}
pal.volcano(parula)
```

## pal.zcurve()

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:
 
1. A good color palette of 64 colors should be able to resolve 4 sub-squares 
within each of the 16 squares. 

```{r}
pal.zcurve(parula, main="parula")
```

# Using palettes with ggplot2

To use any colormap with the `ggplot2` package, use the `scale_fill_gradientn` function as shown in the following example.

```{r}
require(ggplot2)
require(pals)
require(reshape2)
ggplot(melt(volcano), aes(x=Var1, y=Var2, fill=value)) + 
  geom_tile() + 
  scale_fill_gradientn(colours=coolwarm(100), guide = "colourbar")
```

# Catalog of colormaps and palettes

The following images show bands for all the colormaps and palettes in the `pals` package, grouped in 

```{r}
# Discrete
pal.bands(alphabet, alphabet2, cols25, glasbey, kelly, pal36, stepped, tol, watlington,
          main="Discrete", show.names=FALSE)


# Misc
pal.bands(coolwarm,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)

```

