There’s definitely a way to do this in both VegaLite and AoG (including the text), though I don’t think either make it easy to do the randomized dodge you want (you’d have to define a new column that has random offsets).
In AoG you can use the Text plot type (example here) and combine it with the above figure using the + operator. Note that any Makie plot types documented here each have a corresponding CamelCase name that can be passed to visual.
In vega lite I think you would need to use an empty @vlplot() first, e.g. @vlplot() + @vlplot(line fields) + @vlplot(text-label filed), but it’s been a little while since I used it.
That said, if Gadfly meets your needs, maybe you don’t need to look further. There are definitely trade-offs between them.
Thanks a lot @rafael.guerra ! That is impressively close to he ggplot version.
I had given up on StatsPlots because of the large compilation times, but that has really improved tremendously now, so I’ll definitely revisit that.
It would be great to have a simple way to do label colouring, and the non-overlapping label-placement as {ggrepel}, but I’ll probably come back to your solution for inspiration next time I need this.
It’s great that we have so many options in the Julia plotting eco-system, but it is a challenge to keep updated. Any thoughts on which (if any) is going to be the “ggplot for Julia”?
With VegaLite I can get the lines and points, but not the text labels. This is what I try:
using VegaLite
d |> @vlplot() + @vlplot(:line, x = :time, y= :value, color = :name) + @vlplot(:point, x = :time, y= :value, color = :name) + @vlplot(:text, x = :time, y= :value, color = :name)
Thanks @haberdashPI . This does show the lables, but right on top of the points.
Removing the points helps a bit:
using VegaLite
d |> @vlplot() + @vlplot(:line, x = :time, y= :value, color = :name) + @vlplot(:text, x = :time, y = :value, color = :name, text = :name)
Thanks a lot @sijo and @fabgrei . It looks like AoG could become the “ggplot for Julia”. It would indeed be great with a more unified syntax. I’m still not quite sure how to parse the mapping for the Text / Annotations visual. Are they actually the same (Text and Annotations)?
We give :name as positional argument (that’s the column holding the strings). The => verbatim transformation is added so the value is used “as is”.
The position keyword is used to specify the column holding the points. I used a column with pre-computed points, while @fabgrei computed the point with a transformation that takes two values as input: position=(:time, :value) => Point (because the Point method takes two values to make a point).
The offset keyword is used to specify the column holding precomputed offsets. Here it seems we need verbatim again.
The verbatim thing is bit annoying… I don’t know the rule for when it’s required, so I just try to add one if it doesn’t work without. Maybe @piever can explain why and when it’s required…
So, annotations with AlgebraOfGraphics are somewhat annoying for the two reasons described above:
The signature is non-standard (rather than just text(x, y, labels=labels), which would be more in line with scatter)
One of the arguments is a vector of strings, which triggers the need for verbatim.
The first problem should be more straightforward to solve (see this PR).
The second one is trickier. The main issue is that AlgebraOfGraphics treats any vector it does not recognize as “numeric” (mostly AbstractVector{<:Number}) or “geometric” (lists of points, or polygons, etc…) as a “categorical vector”, which in turns triggers a conversion from categorical values to integers (as well as grouping of the data).
verbatim is a way to escape that, because, in the case of annotations or text, the categorical vector (vector of strings) is what we want to pass to the plot directly, as Makie can handle that. There is an issue open to track this, but I’m not yet 100% sure what the best solution is.
One possible way forward (once labels in annotations or text are passed as a keyword argument), could be to only consider a keyword argument categorical if there exists a palette (categorical scale) for it. That does add some complexity (as categorical variables determine how the data is grouped, which is relevant to compute analyses, and would now depend on the choice of scales), but it would avoid the “aggressive conversion pipeline”.
From what I understand, ggplot2 fixes this by “hardcoding” the plot attributes that cause grouping (see here), but I would prefer a more flexible approach. We could still get something like that by default, because those would be variables with built-in palettes.
Update: The signature of Text is now standard, so it takes x, y, text = text which means a mapping call could look like mapping(:x, :y, text = :text => verbatim) * visual(Text)
Is this in an upcoming version, or should it work now?
Doing this:
using AlgebraOfGraphics, GLMakie
using Random, DataFrames
Random.seed!(123)
d = DataFrame(name = repeat(["A","B","C","D","E","F"], inner=4),
time=repeat([0,1,3,6], outer=6), value = rand(24));
plt = data(d) * (visual(Scatter) + visual(Lines) + visual(GLMakie.Text)) * mapping(:time, :value, color = :name, text = :name => verbatim) ;
AlgebraOfGraphics.draw(plt)
I get a stacktrace starting with:
Error showing value of type AlgebraOfGraphics.FigureGrid:
ERROR: MethodError: no method matching gl_convert(::Vector{String})
Closest candidates are:
gl_convert(::T) where T<:ColorTypes.Colorant at ~/.julia/packages/GLMakie/K6iJk/src/GLAbstraction/GLUniforms.jl:194
gl_convert(::Quaternion) at ~/.julia/packages/GLMakie/K6iJk/src/glshaders/particles.jl:30
gl_convert(::StaticArraysCore.SMatrix{N, M, T}) where {N, M, T} at ~/.julia/packages/GLMakie/K6iJk/src/GLAbstraction/GLUniforms.jl:230
Also, if I write visual(Text))
I get this error:
WARNING: both GLMakie and Base export "Text"; uses of it in module Main must be qualified
ERROR: UndefVarError: Text not defined
Use Makie.Text to avoid the ambiguity error, Base has its own Text which is a little annoying for us. And I think the problem is that you use your mapping with text = ... on Scatter, Lines and Text at the same time, so it errors for Scatter or Lines probably which pass through the unused attribute until GLMakie chokes.