1

In a heatmap, how could I create a three-color gradient, with blue for negative values, red for positive values and white for zero, such that with many zero values, much of the heatmap would be white (and not light red as with the default gradient).

A = [5 0 -3 -2 7; 0 5 0 0 0; -2 0 -1 0 0; -4 0 0 -10 0; 0 0 0 0 9]
using Plots
heatmap(Array(A),
    c = cgrad([:blue,:white,:red]),
    yflip = true,
    xlabel = "row", ylabel = "col",
    title = "Nonzeros, Positives, Negatives, in Matrix")

enter image description here

Here the gradient is automatically centered at the midpoint, a sensible default.

Related: here.

Post-Scriptum

As BallPointBen suggests, it would seem that computing the range of values is the recommended approach. Here is some benchmarking. The best approach, suggested by BallPointBen, is max = maximum(abs, A)

julia> using BenchmarkTools
julia> A = rand(10_000, 10_100)

julia> @btime max = maximum(abs.(A))
  432.837 ms (6 allocations: 770.57 MiB)
0.999999999929502

julia> @btime max = maximum(abs.(extrema(A)))
  339.597 ms (5 allocations: 144 bytes)
0.999999999929502

julia> @btime max = maximum(abs, A)
  60.690 ms (1 allocation: 16 bytes)
0.9999999985609005
PatrickT
  • 10,037
  • 9
  • 76
  • 111

1 Answers1

2

You can compute the maximum absolute value in your array, then use it to set the clims argument. c.f. http://docs.juliaplots.org/latest/generated/attributes_subplot/

julia> max_val = maximum(abs, A)
10

julia> heatmap(Array(A),
           c = cgrad([:blue,:white,:red]),
           yflip = true,
           xlabel = "row", ylabel = "col",
           title = "Nonzeros, Positives, Negatives, in Matrix",
           clims=(-max_val, max_val))

enter image description here

BallpointBen
  • 9,406
  • 1
  • 32
  • 62
  • 1
    Neat, thanks. No "built-in" approach for `clims`? A slightly more efficient way for a large Array: `maximum(abs.(extrema(A)))`, since `extrema` gets the min and max in one pass and the `abs()` is over two values rather than over the array's size. – PatrickT Jun 10 '21 at 21:40
  • 1
    @PatrickT Actually I think the preferred way would be to use `maximum(f, A)`. I've updated my answer – BallpointBen Jun 10 '21 at 22:06
  • Absolutely. Added the benchmark. I didn't know about this use of `maximum`. Thanks a lot! – PatrickT Jun 10 '21 at 23:47