# Labels for levels in contour plot (Plots.jl)

**URL:** <https://discourse.julialang.org/t/labels-for-levels-in-contour-plot-plots-jl/8266>\
**Category:** Visualization\
**Tags:** question, plotting, plots\
**Created:** [January 10, 2018, 11:01am UTC](https://discourse.julialang.org/t/labels-for-levels-in-contour-plot-plots-jl/8266 "2018-01-10T11:01:38Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [January 10, 2018, 11:01am UTC](https://discourse.julialang.org/t/labels-for-levels-in-contour-plot-plots-jl/8266/1 "2018-01-10T11:01:38Z")

</div>

I would like to add custom labels to a `contour` plot, replacing the `levels`. Is this possible? MWE:

```julia
using Plots; pyplot() # other backend OK if necessary
using StatsBase
using Distributions
using StatsFuns

N = 100000
z = rand(MvNormal([1.0, 0.0], [1.0 0.5; 0.5 0.5]), N);
x = logistic.(z[1, :]);
y = logistic.(z[2, :]);
h = fit(Histogram, (x, y), closed = :left, nbins = (50, 50));

gap = trunc(Int, maximum(h.weights) / 9)
levels = 0:gap:(10*gap)
level_labels = map(x -> "$(x*10)%", 0:10)

contourf(midpoints(h.edges[1]),
         midpoints(h.edges[2]),
         h.weights';
         levels = levels) # how to put level_labels in?

```

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [January 10, 2018, 12:08pm UTC](https://discourse.julialang.org/t/labels-for-levels-in-contour-plot-plots-jl/8266/2 "2018-01-10T12:08:40Z")

</div>

I realized that for printed output, I don’t need `contourf`, `contour` would do just fine. But how can I

1. make all the lines black, and
2. put labels on them?

Eg continuing the above code,

```julia
contour(midpoints(h.edges[1]),
        midpoints(h.edges[2]),
        h.weights';
        color = "black", # how to change contour lines to black? this does not work?
        colorbar = false,
        levels = levels) # how to put level_labels in?

```

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [January 10, 2018, 12:22pm UTC](https://discourse.julialang.org/t/labels-for-levels-in-contour-plot-plots-jl/8266/3 "2018-01-10T12:22:59Z")

</div>

I realized that I can go to the backend as a workaround:

```julia
using PyPlot
using StatsBase
using Distributions
using StatsFuns

N = 100000
z = rand(MvNormal([1.0, 0.0], [1.0 0.5; 0.5 0.5]), N);
x = logistic.(z[1, :]);
y = logistic.(z[2, :]);
h = fit(Histogram, (x, y), closed = :left, nbins = (20, 20));

gap = trunc(Int, maximum(h.weights) / 9)
levels = collect(0:gap:(10*gap))
level_labels = map(x -> "$(x*10)%", 0:10)

PyPlot.svg(true)
cs = contour(midpoints(h.edges[1]),
             midpoints(h.edges[2]),
             h.weights';
             colors = "black",
             levels = levels)
clabel(cs, fmt = Dict(zip(levels, level_labels)), inline = 1)

```

![x](https://global.discourse-cdn.com/julialang/original/3X/d/7/d781ac65258ab5b14af764f6f14800763e648a1d.png)

---

<div class="post-metadata">

**Author:** ![BLI](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bli/32/37206_2.png) [@BLI](https://discourse.julialang.org/u/BLI)\
**Post date:** [January 10, 2018, 4:59pm UTC](https://discourse.julialang.org/t/labels-for-levels-in-contour-plot-plots-jl/8266/4 "2018-01-10T16:59:29Z")

</div>

A related (?) problem… I have tried to use Plots to do a 3d surface plot with contours (useful, e.g., in illustrating optimization problems). However, I was not able to freely choose the z-position of the contours – this seems to be hard-coded to z=0. If the minimum value of f(x,y) is “small” (close to zero or lower), the surface plot will often hide the contours. When I tried to learn Matplotlib some years ago, I found it was relatively straightforward to set the z-position of the contours, and I’d think this is very useful also in Plots.

[OK – perhaps this is possible in Plots – I just haven’t found any documentation of it.]

---

<div class="post-metadata">

**Author:** ![sschelm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sschelm/32/3745_2.png) [@sschelm](https://discourse.julialang.org/u/sschelm)\
**Post date:** [March 18, 2018, 11:33am UTC](https://discourse.julialang.org/t/labels-for-levels-in-contour-plot-plots-jl/8266/6 "2018-03-18T11:33:06Z")

</div>

Part solutions:  
define constant color map:  
`color = cgrad([:black,:black,:black])`  
this seems to also work (though I’ve seen glitches with this)  
`color = :black`

show contour labels  
`contour_labels = true`

These usages seem to require the latest master.

I haven’t come across a way to use custom labels for the contour levels so far.

---

<div class="post-metadata">

**Author:** ![FabsOliveira](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fabsoliveira/32/9850_2.png) [@FabsOliveira](https://discourse.julialang.org/u/FabsOliveira)\
**Post date:** [August 20, 2019, 7:09am UTC](https://discourse.julialang.org/t/labels-for-levels-in-contour-plot-plots-jl/8266/7 "2019-08-20T07:09:37Z")

</div>

Bumping up since the title is the same I wanted. Tried something similar to what @sschelm suggested. I am using Julia 1.1 and Plots 0.26.2. Full working example is below. Works fine without `contour_labels = true` and used to work with ‘clabels = true’ but now it doesn’t.

Any ideas on how to get that sorted?

```julia
using Plots, LaTeXStrings
pyplot()

n = 500

x1 = range(-1, stop=10, length=n);
x2 = range(-1, stop=10, length=n);

f(x) = (x[1])^2 + (x[2])^2
z = [f([x1[i], x2[j]]) for j = 1:n, i = 1:n];

contour(x1,x2,z,
      levels = [4, 8, 12, 16],
      xaxis = (L"$x_1$", (0,4), (0:1:10)),
      yaxis = (L"$x_2$", (0,4)),
      clims = (1,15),
      contour_labels = true,
      aspect_ratio = :equal
)

```

I get an error that looks like this:

MethodError: no method matching getindex(::Base.Generator{Tuple{Int64,Int64},getfield(Plots, Symbol(“##280#282”)){Float64}}, ::Int64)  
(::getfield(Plots, Symbol(“##279#281”)){Base.Generator{Tuple{Int64,Int64},getfield(Plots, Symbol(“##280#282”)){Float64}},Array{Int64,1}})(::Int64) at none:0  
iterate at generator.jl:47 [inlined]  
mapfoldl\_impl(::Function, ::Function, ::NamedTuple{(),Tuple{}}, ::Base.Generator{UnitRange{Int64},getfield(Plots, Symbol(“##279#281”)){Base.Generator{Tuple{Int64,Int64},getfield(Plots, Symbol(“##280#282”)){Float64}},Array{Int64,1}}}) at reduce.jl:55  
#mapfoldl#187 at reduce.jl:72 [inlined]  
mapfoldl at reduce.jl:72 [inlined]  
#mapreduce#191 at reduce.jl:205 [inlined]  
mapreduce(::Function, ::Function, ::Base.Generator{UnitRange{Int64},getfield(Plots, Symbol(“##279#281”)){Base.Generator{Tuple{Int64,Int64},getfield(Plots, Symbol(“##280#282”)){Float64}},Array{Int64,1}}}) at reduce.jl:205  
minimum(::Base.Generator{UnitRange{Int64},getfield(Plots, Symbol(“##279#281”)){Base.Generator{Tuple{Int64,Int64},getfield(Plots, Symbol(“##280#282”)){Float64}},Array{Int64,1}}}) at reduce.jl:503  
showjuno(::IOContext{Base.GenericIOBuffer{Array{UInt8,1}}}, ::MIME{Symbol(“image/svg+xml”)}, ::Plots.Plot{Plots.PyPlotBackend}) at output.jl:238  
show(::IOContext{Base.GenericIOBuffer{Array{UInt8,1}}}, ::MIME{Symbol(“image/svg+xml”)}, ::Plots.Plot{Plots.PyPlotBackend}) at output.jl:195  
\_\_binrepr at multimedia.jl:129 [inlined]  
\_textrepr at multimedia.jl:119 [inlined]  
#stringmime#6 at Base64.jl:38 [inlined]  
(::getfield(Base64, Symbol(“#kw##stringmime”)))(::NamedTuple{(:context,),Tuple{IOContext{Base.GenericIOBuffer{Array{UInt8,1}}}}}, ::typeof(Base64.stringmime), ::MIME{Symbol(“image/svg+xml”)}, ::Plots.Plot{Plots.PyPlotBackend}) at none:0  
#stringmime#7(::IOContext{Base.GenericIOBuffer{Array{UInt8,1}}}, ::Function, ::String, ::Plots.Plot{Plots.PyPlotBackend}) at Base64.jl:39  
#stringmime at none:0 [inlined]  
displayinplotpane(::Plots.Plot{Plots.PyPlotBackend}) at showdisplay.jl:55  
displayandrender(::Plots.Plot{Plots.PyPlotBackend}) at showdisplay.jl:115  
(::getfield(Atom, Symbol(“##129#134”)))() at eval.jl:142  
#invokelatest#1 at essentials.jl:742 [inlined]  
invokelatest at essentials.jl:741 [inlined]  
(::getfield(Atom, Symbol(“##128#133”)){String,String,Module})() at eval.jl:141  
withpath(::getfield(Atom, Symbol(“##128#133”)){String,String,Module}, ::String) at utils.jl:30  
withpath at eval.jl:46 [inlined]  
#127 at eval.jl:122 [inlined]  
with\_logstate(::getfield(Atom, Symbol(“##127#132”)){String,String,Module}, ::Base.CoreLogging.LogState) at logging.jl:395  
with\_logger at logging.jl:491 [inlined]  
#126 at eval.jl:121 [inlined]  
hideprompt(::getfield(Atom, Symbol(“##126#131”)){String,String,Module}) at repl.jl:75  
macro expansion at eval.jl:120 [inlined]  
macro expansion at dynamic.jl:24 [inlined]  
(::getfield(Atom, Symbol(“##125#130”)))(::Dict{String,Any}) at eval.jl:109  
handlemsg(::Dict{String,Any}, ::Dict{String,Any}) at comm.jl:164  
(::getfield(Atom, Symbol(“##19#21”)){Array{Any,1}})() at task.jl:259

---

<div class="post-metadata">

**Author:** ![willsharpless](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/willsharpless/32/36397_2.png) [@willsharpless](https://discourse.julialang.org/u/willsharpless)\
**Post date:** [September 9, 2022, 7:13pm UTC](https://discourse.julialang.org/t/labels-for-levels-in-contour-plot-plots-jl/8266/8 "2022-09-09T19:13:33Z")

</div>

Bumping this again to see if there is a solution now. The older solutions above do not yield working work-arounds.

I understand from [this](https://github.com/JuliaPlots/Makie.jl/issues/832) discourse that there is a (complicated) way to do this in Makie.jl right now, but I’d prefer to use GR or pyplot.

I would be satisfied with even a simpler solution than the above, to just add a legend with the levels as items and custom labels, but I’m not even sure how to do this. Any ideas?

---

<div class="post-metadata">

**Author:** ![Ininterrompue](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ininterrompue/32/5594_2.png) [@Ininterrompue](https://discourse.julialang.org/u/Ininterrompue)\
**Post date:** [November 21, 2022, 5:19am UTC](https://discourse.julialang.org/t/labels-for-levels-in-contour-plot-plots-jl/8266/9 "2022-11-21T05:19:59Z")

</div>

I’m working on adding a page to the Plots.jl docs for contour plots and I’m wondering if this is still possible. It’d be nice to have the features illustrated on [this](https://matplotlib.org/stable/gallery/images_contours_and_fields/contour_label_demo.html) page. @Tamas_Papp is the workaround that you wrote for having percentages still working? I’ve filed an issue [here](https://github.com/JuliaPlots/Plots.jl/issues/4531).

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [November 21, 2022, 6:40am UTC](https://discourse.julialang.org/t/labels-for-levels-in-contour-plot-plots-jl/8266/10 "2022-11-21T06:40:01Z")

</div>

Not sure, I no longer use PyPlot. I use PGFPlotsX.jl where this is possible.

---

<div class="post-metadata">

**Author:** ![t-bltg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/t-bltg/32/25526_2.png) [@t-bltg](https://discourse.julialang.org/u/t-bltg)\
**Post date:** [November 21, 2022, 10:52am UTC](https://discourse.julialang.org/t/labels-for-levels-in-contour-plot-plots-jl/8266/11 "2022-11-21T10:52:01Z")

</div>

I think modifying [`contour_labels`](https://github.com/JuliaPlots/Plots.jl/blob/master/src/arg_desc.jl#L54) to take a boolean or a `Vector{String}` would be the easiest (and non breaking).

It is only a matter of someone submitting a `PR`.

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [November 21, 2022, 5:49pm UTC](https://discourse.julialang.org/t/labels-for-levels-in-contour-plot-plots-jl/8266/12 "2022-11-21T17:49:42Z")

</div>

Using the [Contour.jl](https://juliageometry.github.io/Contour.jl/stable/tutorial.html) package, we can annotate individual contours to get:

 ![Plots_Contour_manual_label](https://global.discourse-cdn.com/julialang/original/3X/a/b/ab84babdca1f913b17158bf3e17899bedfd5ca80.png)
