Distinct color for missing values in colorbar

I am wondering how to handle missing values in a colorbar.

For my specific use case, I want to create a scatter plot where the data points are colored according to a third vector of values which may contain missing. Before opening an issue, I am first checking if there is already a way to achieve this, that I have, for lack of better word, missed.

From what I can see, the only way I could do it right now is to loop over x, y and marker_z simultaneously, and exclude values from all arrays for indices that satisfy ismissing(marker_z[i]). This seems a bit silly, since up to the visualisation stage, missing values were propagated through the processing without any issue.

There is one solution that I don’t think is curently possible. First, I relpace missing values with a number I know to be outside the valid cmap range, say:

cmap = replace(x -> ismissing(x) ? 0 : x, cmap_vector)

Then, I would need to combine two colorbars: at the bottom there is a single color for say (0, 10) which is a small range for which the colormap value is meaningless, and above that I have a gradient map as usual.

Alternatively, perhaps this requires a new keyword argument to cgrad, something like missingcolor? Then a small box could be drawn below the colorbar filled with missingcolor and with an appropriate label… Maybe it’s too complicated.

I’m not sure what I think about this, and I wonder if anyone else has some ideas.

Leon

For Plots.jl you could use some workaround like here below:

using Plots; gr()
Z = Vector{Union{Float64, Missing}}(undef, 11)
X = rand(11); Y = rand(11); Z .= -5:5
Z[rand(1:10,3)] .= missing

using Colors, ColorSchemes
c1 = colorant"red"
c2 = colorant"green"
c0 = colorant"white"   # missings will be labelled white
n = 10
cscheme1 = ColorScheme([c0; range(c1, stop=c2, length=n)])
z1, z2 = extrema(skipmissing(Z))
dz = (z2 - z1)/n
Z2 = replace(Z, missing => z1 - dz)
ϵ = 1e-3   # fudge factor
Plots.scatter(X,Y, marker_z=Z2, ms=6, msw=0.3, label=false, c=palette(cscheme1,n+1), clims=(z1-(1+ϵ)*dz,z2+ϵ*dz))

Please double-check the details.

It is easier to plot first the non-missing and then the missing, but then the missings’ color does not make it to the color scale as in workaround above:

using Plots; gr()
Z = Vector{Union{Float64, Missing}}(undef, 11)
X = rand(11); Y = rand(11); Z .= -5:5
Z[rand(1:11,3)] .= missing
ix = ismissing.(Z)
Plots.scatter(X[.!ix],Y[.!ix],marker_z=Z[.!ix], label=false)
Plots.scatter!(X[ix],Y[ix], mc="white", label=false)

Thanks! These are both good options, I didn’t realise I could use logical indexing like that for the second case, pretty cool. I’ll mark the first option as a solution since it adresses the colorbar as well.

1 Like

Just in case, some scatter plots may look better using the theme(:dark):