New colorscheme for Plots.jl 2.0

For the upcoming Plots.jl 2.0 we are thinking about changing the default colorschemes. We identified 3 potential candidates for a new color palette and 4 potential gradients. You can see the combinations in action in this post on github.

Please cast your vote here:

Palette
  • tol_bright
  • tol_light
  • seaborn_colorblind
  • A different one (post your answer)
0 voters
Gradient
  • devon
  • lajolla
  • navia
  • lipari
  • A different one (post your answer)
0 voters
  • cmocean, matter
  • cmocean, haline
0 voters
5 Likes

Graphics libraries like matplotlib, plotly, bokeh, altair, have as default gradient a perceptually uniform
colorscheme, but not one defined according to a color model that has more than 40 years (Crameri’s scientific colormaps, Kovesi colormaps are defined using such a model).
These libraries (matplotlib, plotly, bokeh, altair, etc) have as default sequential colorscheme one defined according to a colormodel, presented at the link that follows, i.e. the same colormodel used in the definition of viridis, plasma, inferno, magma, and cmocean colormaps). More details here https://discourse.julialang.org/t/a-good-colorscheme-encodes-the-right-information-on-data-is-inferno-a-good-colorscheme/99962.

Image illustrating that lajolla, devon, navia and lipari are not perceptually uniform:

A good choice could be one of the sequential colormaps from cmocean package:

6 Likes

Where can be found the definition of lipari and navia to check their lightness? A search in github repo, ColorSchemes.jl, doesn’t return a data file with these names?

Github doesn’t search files larger than about 300KB. Because ColorSchemes/data/scicolor.jl is 827KB, it can’t be searched for in Github.

1 Like

I’ve succeeded to get the files and inserted the plots of their lightness as can be seen above. Thanks!

1 Like

Do I understand correctly that what we want is a colormap with low variation in the perceptual differences between adjacent colors? If DE_2000 is a good metric of these differences then we can look at the standard deviation to find those with low variation?

using GLMakie, IterTools
firstarg(x, xs...) = x
let step = 1
    metric = DE_2000()
    syms = [:viridis, :inferno, :devon, :tokyo, :matter, :ice, :deep, :haline]
    fig = Figure()
    css = map(syms) do s
        getproperty(ColorSchemes, s).colors[begin:step:end]
    end
    dss = map(css) do cs
        IterTools.partition(cs, 2, 1) .|> splat((a,b) -> colordiff(a,b; metric))
    end
    order = sortperm(dss; by=std)
    syms = syms[order]
    css = css[order]
    dss = dss[order]
    Label(fig[1,1], "Perceptual differences\n$(typeof(metric))(colormap[$(step == 1 ? "" : step)n], colormap[$(step == 1 ? "" : step)(n+1)])"; fontsize=20, tellwidth=false)
    gl = GridLayout(fig[2,1], length(syms)+1, 4; font="Noto Sans")
    Label(gl[1,1], "colormap"; font="Noto Bold")
    Label(gl[1,2], "mean"; font="Noto Bold")
    Label(gl[1,3], "std"; font="Noto Bold")
    axes = map(enumerate(zip(syms, css, dss))) do (i, (sym, cs, ds))
        Label(gl[i+1,1], string(sym); tellheight=false, font="Noto Bold")
        Label(gl[i+1,2], string(round(mean(ds); digits=2)), tellheight=false)
        Label(gl[i+1,3], string(round(std(ds); digits=2)), tellheight=false)
        ylims = (0, 1.2step)
        ax = Axis(gl[i+1,4], limits=(nothing, ylims))
        heatmap!(ax, eachindex(cs), range(ylims[1], ylims[2]; length=10), firstarg; colormap=cs)
        lines!(ax, ds; color=:white)
        # lines don't support stroke (https://github.com/MakieOrg/Makie.jl/issues/1597), so hack it.
        lines!(ax, ds .- .02; color=:black, linewidth=.7)
        lines!(ax, ds .+ .02; color=:black, linewidth=.7)
        ax, std(ds)
    end
    save("/tmp/fig.png", fig; px_per_unit=2, size=(1200,800))
    fig
end

I think turbo is intended to make small differences visible, so I guess there’s an opportunity for comparing the std to the mean adjacent difference. As expected, turbo gives away some uniformity to get some more contrast.

let metric = DE_2000()
    syms = [:viridis, :inferno, :devon, :tokyo, :hawaii, :buda, :matter, :ice, :deep, :haline, :turbo]
    css = map(syms) do s
        getproperty(ColorSchemes, s).colors
    end
    dss = map(css) do cs
        IterTools.partition(cs, 2, 1) .|> splat((a,b) -> colordiff(a,b; metric))
    end
    fig = Figure()
    ax = Axis(fig[1,1]; title="$(typeof(metric)) adjacent differences", xlabel="mean", ylabel="std")
    text!(ax, sum.(dss)/256, std.(dss); text=string.(syms), align=(:center, :baseline))
    fig
end

@peterkovesi who developed GitHub - peterkovesi/PerceptualColourMaps.jl: Perceptually Uniform Colour Maps for Julia might have some thoughts as well.

1 Like

I strongly second that the default gradient should be perceptually uniform.

1 Like

I recall that a colorscheme is perceptually uniform if transforming each RGB color in its definition to a color in the space CAM02-UCS, of coords (lightness, chroma, hue), the lightness is linear. This means that the graph of the function that associates to each normalized value x\in [0,1] the lightness if its corresponding color in the colorscheme is a line. In the plots posted above the graph of lightness of the lajolla, devon, navia and lipari are not straight lines.

1 Like

+1 for perceptual uniformity. I like the magma colormap a lot, and the similar Cmocean matter. Viridis and haline are good too.

Nice effort! – Thanks.