Plotting different markers with legend per marker

Hi all, my MWE is this one

using Plots
rows = Matrix([
        "s"  "tokyo"        8918018
        "s"  "delhi"        2605537
        "b"  "shanghai"      705818
        "a"  "sao paulo"     580568
        "a"  "mexico city"   379802
        "s"  "cairo"         343985
        "b"  "mumbai"        279183
        "b"  "beijing"       168452
        "b"  "dhaka"         123337
        "b"  "osaka"         117008
])
shapeDict = Dict("s" => :star, "u" => :diamond, "a" => :circle, "b" => :hexagon);
shapes = [shapeDict[src] for src in rows[:, 1]];
scatter(rows[:, 2], rows[:, 3], 
        xticks = :all,
        rotation=30,
        markersize = 10,
        size=(600, 400),
        title = "Count per city", 
        markershape = shapes
)

What I get is this:

image

What I would like is to add description of the different markers to the legend. Now there is only star, but I’d like to add also the other 2.

I tried some unsatisfying workaround

scatter(
.......
legend = [string(:star) * " like apples"
                string(:diamond) * " like oranges"
                string(:hexagon) * " like bananas"]
)

but even that didn’t work (ERROR: MethodError: no method matching convertLegendValue(::String))

Any idea please?

(@v1.4) pkg> status
Status `C:\Users\u\.julia\environments\v1.4\Project.toml`
  [336ed68f] CSV v0.6.2
  [35d6a980] ColorSchemes v3.9.0
  [a93c6f00] DataFrames v0.21.0
  [864edb3b] DataStructures v0.17.17
  [e7dc6d0d] DataValues v0.4.13
  [7806a523] DecisionTree v0.10.1
  [b4f34e82] Distances v0.8.2
  [31c24e10] Distributions v0.23.3
  [587475ba] Flux v0.10.4
  [8d5ece8b] GLMNet v0.5.1
  [cd3eb016] HTTP v0.8.14
  [09f84164] HypothesisTests v0.10.0
  [7073ff75] IJulia v1.21.2
  [6218d12a] ImageMagick v1.1.5
  [916415d5] Images v0.22.2
  [682c06a0] JSON v0.21.0
  [a93385a2] JuliaDB v0.13.0
  [2c06ca41] JuliaDBMeta v0.4.2
  [5ab0869b] KernelDensity v0.5.1
  [b1bec4e5] LIBSVM v0.4.0
  [50d2b5c4] Lazy v0.15.0
  [093fc24a] LightGraphs v1.3.3
  [23992714] MAT v0.8.0
  [f0e99cf1] MLBase v0.8.0
  [4f449596] MatrixNetworks v1.0.0
  [442fdcdd] Measures v0.3.1
  [e1d29d7a] Missings v0.4.3
  [b8a86587] NearestNeighbors v0.4.4
  [b98c9c47] Pipe v1.2.0
  [58dd65bb] Plotly v0.3.0
  [91a5bcdd] Plots v1.2.2
  [438e738f] PyCall v1.91.4
  [1a8c2f83] Query v0.12.2
  [ce6b1742] RDatasets v0.6.8
  [2913bbd2] StatsBase v0.33.0
  [f3b207a7] StatsPlots v0.14.6
  [bd369af6] Tables v1.0.4
  [a7f2b756] ThinkJulia v0.0.0 #master (https://github.com/BenLauwens/ThinkJulia.jl)
  [b8865327] UnicodePlots v1.1.0
  [0ae4a718] VegaDatasets v2.1.0
  [112f6efa] VegaLite v2.1.3
  [fdbf4ff8] XLSX v0.7.1
  [37e2e46d] LinearAlgebra
  [9a3f8284] Random
  [2f01184e] SparseArrays
  [10745b16] Statistics

One idea is to overlay a few sacrificial plots and build the legend line-by-line:

using Plots
gr()
rows = Matrix([
        "s"  "tokyo"        8918018
        "s"  "delhi"        2605537
        "b"  "shanghai"      705818
        "a"  "sao paulo"     580568
        "a"  "mexico city"   379802
        "s"  "cairo"         343985
        "b"  "mumbai"        279183
        "b"  "beijing"       168452
        "b"  "dhaka"         123337
        "b"  "osaka"         117008
])
shapeDict = Dict("s" => :star, "u" => :diamond, "a" => :circle, "b" => :hexagon);
shapes = [shapeDict[src] for src in rows[:, 1]];
custommarkerplot = scatter(rows[:, 2], rows[:, 3], 
        xticks = :all,
        rotation=30,
        markersize = 10,
        size=(600, 400),
        title = "Count per city", 
        markershape = shapes,
        label="",
        color=:blue

)
scatter!(1,1, markershape=:star, color=:blue, label="the star series")
scatter!(1,1, markershape=:hexagon, color=:blue, label="the hexagon series")
scatter!(1,1, markershape=:circle, color=:blue, label="the circle series")

savefig(custommarkerplot, "custommarkerplot.png")

custommarkerplot

That’s great. This pretty much solves my problem. Thank you!

Besides that, can I ask you, what those 1, 1 in scatter!(1,1, markershape=:star, color=:blue, label="the star series") mean?

I tried scatter(10...) and it plotted the legend 10 times. According to documentation - Basics · Plots the number means count of series. But what about the second 1?

To be honest, I’m sometimes confused by the versatility of arguments.
E.g.

scatter(1, 1, ...)
vs.
scatter([1], [1], ...)

I’m comming from statically typed languages (.NET world) and I can quite easily determine what method will be caled, based on the arguments I pass. Here I’m a little bit lost.

I understand completely. Plots.jl can be tricky, like many other plotting packages.

To answer your question, I do not know why I have a second “1” in those scatter! lines. I had this method of building a legend stored away in a file of my “helper functions” for several years now. So maybe at one time the second “1” was required - I do not remember. But as you probably already figured out, the code works without the second “1”.

Hi,

Unfortunately this solution does not work anymore, the marker is not displayed because Plots considers that data are empty… It works if you replace 1 by [1] but it adds a point on the plot…

You could use the following workaround:

# (continued...)
xl, yl = xlims(custommarkerplot), ylims(custommarkerplot)
scatter!([-1],[-1], xlims=xl, ylims=yl, ...)
# etc.