xotix
xotix

Reputation: 530

Assign color to values

Let's say I have code that generates me a 2D array with values 0,1,2 or 3. E.g.

x = rand(Int8.(0:3), (4,4))

and now I want to plot that on a 2D grid whereas we assign each value a color, e.g.:

0: white, 1: green, 2: blue, 3: red

Example:

Assume I have the matrix

0 3
2 1

then I'd like to have the following image:

enter image description here

now I thought I could use heatmap but the following code

using Plots
data = [0 3; 2 1]
heatmap(data, c=cgrad([:white, :green, :blue, :red], categorical=true))

gives me enter image description here

As you can see, it "plots" the values of the matrix along the axis. The axis imply an ordering of my data and thus my image gets flipped and rotated.

How can I do that?

Upvotes: 2

Views: 765

Answers (2)

MBaz
MBaz

Reputation: 258

I present a solution with Gaston.jl, which uses gnuplot as a backend.

A common way to solve a problem like this using gnuplot is to define a custom palette and plot the matrix as an image. Gaston sets up the axes so that the matrix "looks" the same mathematically and as an image; in other words, element [1,1] is drawn on the left top corner; the element on the last row and last column is drawn on the bottom right corner.

So, this works (at first sight):

using Gaston
M = [0 3; 2 1]
pal = "defined (0 'white', 1 'green', 2 'blue', 3 'red')"
imagesc(M, Axes(palette = pal, colorbox = :off, tics = :off))

enter image description here

A difficulty with this approach is that gnuplot scales both the arrray elements and the palette to the range [0,1]. So, the extremes of the palette are always assigned to the minimum and maximum elements of the array. So, the following example fails:

M = [1 0; 0 1];
pal = "defined (0 'white', 1 'green', 2 'blue', 3 'red')"
imagesc(M, Axes(palette = pal, colorbox = :off, tics = :off))

enter image description here

We would like to see two green squares instead of two red ones.

A possible solution is to programatically generate a palette that works. Assuming that we know in advance the range of (integer) elements of the array and the palette, the following function calculates a custom gnuplot palette that fits the matrix being plotted:

const colors = Dict(0 => "white", 1 => "green", 2 => "blue", 3 => "red")

function define_pal(M)
    entries = unique(M) |> sort
    pal = "defined ("

    for e in entries
        pal *= "$e '$(colors[e])'"
        if e == entries[end]
            pal *= ")"
        else
            pal *= ", "
        end
    end
    return pal
end

Two examples:

M = [1 0; 0 1];
imagesc(M, Axes(palette=define_pal(M), colorbox=:off, tics=:off))

enter image description here

and

M = [1 3; 3 1];
imagesc(M, Axes(palette=define_pal(M), colorbox=:off, tics=:off))

enter image description here

Note that Gaston requires having gnuplot installed.

Upvotes: 1

Bogumił Kamiński
Bogumił Kamiński

Reputation: 69949

You can write e.g.

using Plots
heatmap(x, c=cgrad([:white, :green, :blue, :red], categorical=true))

if you want to disable axis and legend and reverse the rows write (using matrix operations):

heatmap(reverse(x, dims=1),
        c=cgrad([:white, :green, :blue, :red], categorical=true),
        axis=nothing, legend=:none)

or using heatmap kwarg (I retain the axis to show that they are flipped)

heatmap(x, yflip=true, c=cgrad([:white, :green, :blue, :red], categorical=true))

Upvotes: 2

Related Questions