Plotting "hierachically" organized variables

Dear community,
is there an elegant/easy/concise way of plotting some y variables against an x variable, where y variables should either be divided into facets or colors, i.e.

for a dataframe and defined facets:

a=randn(10);
b = a .+ 1;
c = 2a

d = randn(10);
e = d .-1

df = DataFrame(;time=1:length(a), a, b, c, d, e)
facets=[[:a, :b, :c], [:d, :e]]

I came up with a cumbersome(?) function:

function vari_facets(df, x, facets)
    function assign_facet(v)
        fidx = findall(facets) do fa
            v in String.(fa)
        end
        fidx = isnothing(fidx) ? missing : fidx
        return fidx
    end
    dflong = DataFrames.stack(df, Not(x))
    
    @transform! dflong @astable :facet =  assign_facet(:variable)
    print(dflong)

    plts = map(enumerate(facets)) do (i, fa)
        p = data(@subset(dflong |> dropmissing, i in :facet)) * mapping(x, :value, color=:variable) * visual(Lines)
     end
    
     fig=Figure(resolution=(1920,1080))
    
     for i in 1:length(plts)
        g=draw!(fig[i,1], plts[i], axis=(; xlabelvisible=i == length(plts)))
        legend!(fig[i,2],g)
     end
    fig
end

vari_facets2(df, :time, facets)

which creates the figure above.

Is there are better more idiomatic way I might be missing?
Thank!

Coming from ggplot I like AlgebraOfGraphics for such types of things:

using AlgebraOfGraphics, CairoMakie

tofacet(x) = "Facet_" * string(findfirst(in.(Symbol(x), facets)))
data(DataFrames.stack(df, Not(:time))) * mapping(:time, :value, color = :variable, row = :variable => tofacet) * visual(Lines) |> draw

Sorry, missed that you already use AlgebraOfGraphics and ignored your requirement to get a legend per group. Don’t think there is a simpler solution than building the plots per group and drawing the respective subplots explicitly.
The first task can be simplified using the wide data specification though:

# This comprehension replaces all of your function till `fig=...`
plts = [data(df) * mapping(:time, grp .=> :value, color = dims(1) => renamer(grp)) * visual(Lines)
        for grp in facets]

Hope that actually helps.

1 Like