Scatter plot with multiple labels per point

Suppose I have some a set of items like this:

stuct DummyItem
 x::Float64
 y::Float64
 labels::Vector{String}
end

Where labels can have 10-100 elements but only up to max 10 or so unique values.

Is there a good way to visualize a set of DummyItems (size of set is expected to be in order of 100 items)?

One way I could think of is to make a scatter plot with the x and y values and let each point be a pie-chart (and the legend will then be one color per unique label string). I’m of course open to better ways to do it.

Is there a way to make such a plot? I’m currently using Plots.jl with the GR backend, but I’m not locked in to it.

Context in case it matters: I’m currently looking into making the optimizer rule a per-layer search space in NaiveGAflux. The animation on the readme has an example of how the plot looks today with a single optimiser per model. The change means there will be a population of arbitrary neural network architectures where each architecture has one set of optimizer hyperparameters per operation with trainable parameters.

This is the brute-force:ish solution I ended up with when I got around to it. Could probably be made easier with plot recepies, but for now I just want to see if this is at all useful to display my data.

# Draws a single pie chart with center at (x,y)
function plotpie!(plt, labs, x,y; seen, colors, markersize=0.1)
       ulabs = unique(labs)
       fracs = [2pi * count(==(lab), labs) / length(labs) for lab in ulabs]
       start = 0.0
       sectors = similar(fracs, Shape)
       for i in eachindex(fracs)
           sectors[i] = Shape(vcat((x,y), map(p -> p .+ (x,y), Plots.partialcircle(start, start + fracs[i], 50, markersize)), (x,y)))
       start += fracs[i]
       end
       for (sector, lab) in zip(sectors, ulabs)
           maybec = get(seen, lab, nothing)
           color, label = if isnothing(maybec)
               color = colors[length(seen)+1]
               seen[lab] = color
               color, lab
           else
               maybec, false
           end
           plot!(plt, sector; label, color, linecolor=color)
       end
       plt
end

# Entry point
function plotpie(ps)
       ulabs = unique(mapreduce(p -> p.labels, vcat, ps))
       colors = palette(:jet, length(ulabs))
       seen = Dict()
       plt = plot()
       for p in ps
        plotpie!(plt, p.labels, p.x, p.y; seen, colors)
       end
       plot!(plt, aspect_ratio=:equal, legend=:outertopright)
       plt
end

# Test it out
genitem(n) = (labels = string.(rand('a':'f', n)), x = randn(), y = randn())

plotpie([genitem(rand(1:100)) for _ in 1:20])

image