One of the nicest features of heatmaps in the Python’s Seaborn module is the ability to annotate heatmaps with the values in the cells, the axes labels, and the line style separating the cells as you can see in these examples: seaborn.heatmap — seaborn 0.12.2 documentation
Given the following heatmap in Plots.jl:
using Plots
A = rand(10,10)
heatmap(A)
Is there an option to annotate the cells? What about the line styles?
1 Like
You can add the elements manually - check: https://juliaplots.github.io/examples/pyplot/#annotations for the text annotations (use font to set the style), and you can overlay lines and use all the normal attributes to set the style https://juliaplots.github.io/attributes/ .
A nice trick is to use the userplot macro to create a recipe https://juliaplots.github.io/recipes/ - you’d need to creat two series (of type heatmap and path, using the series
macro), and specify the annotations using the := syntax. You’d need Plots (not just RecipesBase) to define the recipe to have access to the font
type.
EDIT: passing the annotations in a recipe will require some fiddling.
1 Like
An example speaks louder than a thousand words.
using Plots
@userplot RecipesAreEasy
@recipe function f(x::RecipesAreEasy; annotationargs = ())
y = x.args[1] #Get the input arguments, stored in x.args - in this case there's only one
typeof(y) <: AbstractMatrix || error("Pass a Matrix as the arg to heatmap")
grid := false # turn off the background grid
@series begin # the main series, showing the heatmap
seriestype := :heatmap
y
end
rows, cols = size(y)
#horizontal lines
for i in 0:cols # each line is added as its own series, for clearer code
@series begin
seriestype := :path
primary := false # to avoid showing the lines in a legend
linecolor --> :white
[i, i] .+ 0.5, [0, rows] .+ 0.5 # x and y values of lines
end
end
for i in 0:rows
@series begin
seriestype := :path
primary := false
linecolor --> :white
[0, cols] .+ 0.5, [i,i] .+ 0.5
end
end
@series begin
seriestype := :scatter
markerstrokecolor := RGBA(0,0,0,0.) # make the points transparent - setting marker (or seriestype) to :none doesn't currently work right
seriescolor := RGBA(0,0,0,0.) # do
series_annotations := text.(1:(cols*rows), annotationargs...)
primary := false
repeat(1:cols, inner = rows), repeat(1:rows, outer = cols)
end
end
Then
using Plots; pyplot() # currently broken in gr :-(
recipesareeasy(reshape(1:40, 8,5), annotationargs = (10, "Arial", :lightgrey)) #you can also use linecolor , linestyle, linewidth etc to modify the lines
8 Likes
Thanks @mkborregaard, this is really helpful! I appreciate the comments as well, they helped me a lot in understanding the machinery of plot recipes.
Should this recipe be part of PlotRecipes.jl? It may be useful to other users in the future, plus there is more customization that can be taken into account like the color of the annotations adapting to the heatmap (increase contrast), passing DataFrames, etc.
I think it’d be nicer to allow seriesannotations
and linewidth / -style / -color
to have this functionality inside Plots. It’s intuitive and would give some keywords that don’t apply to heatmap a sensible use. That’s a lot more complex to adjust but a better long-term solution. Nice chance for a new contributor to make a high-value PR
1 Like
Thank for the content. In order to place the values of the matrix in the output figure I have modified this line from ```
series_annotations := text.(1:(cols*rows), annotationargs…)
to ```
series_annotations := text.(vec(round.(y,digits=3), annotationargs...)
Thanks again for sharing!!!
1 Like