Alternate color for each point in scatter plot and add legend

I’m plotting a series where I’d like to alternate colors for each point. This works fine via the mc keyword, but I can’t get the legend to indicate the color coding scheme correctly:

using Plots
a = rand(10); b = rand(10);
scatter(a, b, mc = [:navy, :crimson], label = ["odd", "even"])

hi

How can I fix the legend?

Just plot the two sets separately?

This is fine in the two-color case, but suppose I wanted to code them based on their index mod 5.

You can do a for loop over the 5 sets.

1 Like

I think you need to transpose the label vector

1 Like

@dpsanders Of course, if there’s no way to accomplish this via the label keyword, one can write a for loop. But this is a rather inelegant solution, don’t you agree?

using Plots

function plotter()
    a = rand(100); b = rand(100);
    idx_container = [i:5:100 for i in 1:5]
    colors = [:navy, :crimson, :olivedrab, :rebeccapurple, :goldenrod]
    labels = ["$i mod 5" for i in 1:5]

    fig = plot()

    for (i, idx) in enumerate(idx_container)
        scatter!(fig, a[idx], b[idx], mc = colors[i], label=labels[i])
    end
    return fig
end

plotter()

hi

You have to create a one array (or @view) for the 1st, 6th, 11th entries, another for the 2nd, 7th, and 12th, etc. This results in code far less legible than a list of labels, and calls plot more times then seemingly necessary.

@nilshg Removing the comma from the label vector doesn’t work, unfortunately:

hi

plots still regards the data as only one series, labeled “odd.”

1 Like

The for loop doesn’t seem so bad to me, but you could reshape? Something like:

group_by_mod(x, n) = reshape(x, (n, :))'

x = repeat(1:5, outer=5)
y = repeat(1:5, inner=5)

scatter(group_by_mod.((x, y), 5)...)

image

1 Like

Ah fair enough - I have to agree with Plots, you are passing just one series. The best best is then probably reshaping as above or just passing two series:

julia> scatter([a[1:2:end], a[2:2:end]], [b[1:2:end], b[2:2:end]], mc = [:navy :crimson], label = ["odd" "even"])

But if you are looking to plot loads of series, reshape is the way to go I’d think.

That seems like a nice, clear solution to me.

1 Like

Oh, or group. group is probably what you want.

scatter(x, y, group=repeat('a':'e', 5))

image

4 Likes

This is exactly what I was hoping for! Thank you. Always pleasant not to have to reinvent the wheel for tasks like this.

The comprehension option below is compact enough and allows controlling the markers’ colors, sizes, shapes, etc.:

using Plots; gr() 
N=100; a=rand(N); b=rand(N);
Nc=5; colors=[:navy, :crimson, :olivedrab, :rebeccapurple, :goldenrod]
mshape=[:circle, :rect, :star5, :diamond, :hexagon]
labels=["$i mod $Nc" for i in 1:Nc]
fig = plot(legend=:outertopright)
[scatter!(fig, a[j:Nc:end], b[j:Nc:end], mc=colors[j], label=labels[j], shape=mshape[j], ms=j+3) for j in 1:Nc]
display(fig)

4 Likes

Nice solution! I personally prefer to avoid array comprehensions containing functions with side effects and just write a for loop instead.

6 Likes