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”.

1 Like

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.
1 Like