AlgebraOfGraphics.jl
AlgebraOfGraphics.jl copied to clipboard
Custom recipes arguments converted aggressively
Given the below recipe Makie will dispatch to the right method when given input x of type CategoricalVector to myrecipe(x, y).
using CairoMakie, AlgebraOfGraphics, CategoricalArrays
@recipe(MyRecipe, x, y) do _
Attributes()
end
function Makie.plot!(p::MyRecipe)
Makie.@extract p (x, y)
scatter!(p, x, y)
return p
end
function Makie.plot!(p::MyRecipe{<:Tuple{<:CategoricalVector,Any}})
Makie.@extract p (x, y)
map(x, y) do x, y
violin!(p, levelcode.(x), y)
end
return p
end
But when used as visual(MyRecipe) the x is of type Vector{Int} regardless of the input types. The inputs seem to always be reduced to barest possible representation.
Is this considered expected behaviour, or a fixable issue?
AoG version v0.5.4 and CairoMakie version v0.6.6.
This is the most problematic bit in the interaction with Makie. I imagine that more often than not, the user wants categorical variables to be passed to Makie as integers (which Makie can handle), so by default everything that is not a vector of numbers or a vector of geometric objects (points, polygons, etc.) gets converted to Vector{Int} and AoG handles relabeling.
This ends up being problematic in a few scenarios, as one can potentially have many custom recipes that use custom types, which we do not want to convert to ints. Viceversa, sometimes the user passes vectors of numbers (which are not converted by default), but they were actually supposed to be used as categories.
The current stopgap solution is to use verbatim and nonnumeric (see docs) to signal that a categorical variable should not be converted or, viceversa, that a continuous variable should be converted using the categorical machinery. So, in practice, here you could call data(df) * visual(MyRecipe) * mapping(:x => verbatim).
I am not 100% satisfied with this solution and would be happy to discuss alternatives. I imagine possible alternatives are the following.
- Distinguish between positional and optional arguments and don't convert positional arguments by default (tricky, because at the moment Makie does not support categorical axes, so AoG does need to handle them).
- Do not convert anything by default, and require the user to always use
mapping(:x => factor)or some other name if they want the AoG machinery to be applied. It could be annoying to typefactormany times, as I think could happen in most practical scenarios.
Assuming that Makie ends up supporting categorical axes, one could even consider a different keyword than mapping called eg grouping where one would put columns where the AoG machinery needs to be applied. It actually used to be like that in StatsMakie, see the difference between Group and Style (now mapping) here.