Plots.jl icon indicating copy to clipboard operation
Plots.jl copied to clipboard

Customize errorbars (in particular their caps)

Open Krastanov opened this issue 6 years ago • 27 comments

Is it possible to change size or completely remove the errorbar caps? Currently the defaults are ok, but they do not work great for all plots.

Krastanov avatar Aug 22 '19 20:08 Krastanov

errorbar caps are controlled by the marker group of arguments. markerstrokecolor = :transparent and markersize = 2 should be relevant things to try.

mkborregaard avatar Aug 23 '19 07:08 mkborregaard

But markerstrokecolor=:transparent removes both the cap and the error line itself (under GR)... Is it supposed to do that?

Krastanov avatar Aug 24 '19 00:08 Krastanov

under the pyplot the caps have the color of marker, not the stroke,

image

pyplot() # the caps would be red in gr()
plot(rand(10), yerr=rand(10), lab="data", l=nothing, m=(4,stroke(:red, 1.5)))

It would be nice to be able to customize the caps.

mmikhasenko avatar Jan 15 '20 13:01 mmikhasenko

You have another option of setting ms=0 that defaults to no marker, which I think looks better

isentropic avatar Jun 24 '20 09:06 isentropic

@Krastanov

isentropic avatar Jun 24 '20 09:06 isentropic

Another thing that, as far as I know, is not possible it to change the vertical thickness of the line. That would be very helpful...

JonasIsensee avatar Jul 06 '20 15:07 JonasIsensee

I think lw, msw or some other thing does it

isentropic avatar Jul 07 '20 00:07 isentropic

hm, does not look like it. (So, msw changes the thickness of the vertical line, but not the thickness of the horizontal one)

using Plots
y = rand(10); yerr=0.2rand(10)
p1 = plot(y, yerr=yerr)
p2 = plot(y, yerr=yerr, msw=5, ms=20)
p3 = plot(y, yerr=yerr, ms=20)
p4 = plot(y, yerr=yerr, lw=10)
plot(p1,p2,p3,p4, layout=(2,2))

errorbars

JonasIsensee avatar Jul 07 '20 05:07 JonasIsensee

What backend are you using

isentropic avatar Jul 07 '20 06:07 isentropic

The issue is that the horizontal bar is just a _ marker and not all backends support it

isentropic avatar Jul 07 '20 06:07 isentropic

pyplot

JonasIsensee avatar Jul 07 '20 06:07 JonasIsensee

I found a solution that works in my case but does (so far) not cover all cases. test

The problem is that the currently the caps are drawn using a markershape=:hline. This can be problematic, see https://github.com/JuliaPlots/Plots.jl/issues/2823 And you can not specify length and thickness separately.

My code modifies the yerror recipe to compute the caps as small line segements that are plotted along with and in the style of the vertical lines. It also takes an extra keyword argument specifying the length of the caps.

function errorcap_coords(errorbar, errordata, otherdata; capsize)
    ed = Vector{Plots.float_extended_type(errordata)}(undef, 0)
    od = Vector{Plots.float_extended_type(otherdata)}(undef, 0)#[Vector{float_extended_type(odi)}(undef, 0) for odi in otherdata]
    for (j, (edi, odj)) in enumerate(zip(errordata, otherdata))
        #for (i, edi) in enumerate(errordata)
            #odi = _cycle(odj, i)
            e1, e2 = Plots.error_tuple(Plots._cycle(errorbar, j))
            Plots.nanappend!(ed, [edi - e1, edi - e1])
            Plots.nanappend!(ed, [edi + e2, edi + e2])
            Plots.nanappend!(od, [odj-capsize/2, odj+capsize/2])
            Plots.nanappend!(od, [odj-capsize/2, odj+capsize/2])
        #end
    end
    return (ed, od)
end

@recipe function f(::Type{Val{:yerror}}, x, y, z)
    Plots.error_style!(plotattributes)
    #markershape := :hline
    yerr = Plots.error_zipit(plotattributes[:yerror])
    if z === nothing
        plotattributes[:y], plotattributes[:x] = Plots.error_coords(yerr, y, x)
        errcapy, errcapx = errorcap_coords(yerr, y, x; capsize=get(plotattributes, :capsize, 0.1))
        Plots.nanappend!(plotattributes[:y], errcapy)
        Plots.nanappend!(plotattributes[:x], errcapx)
    else
        plotattributes[:y], plotattributes[:x], plotattributes[:z] =
        Plots.error_coords(yerr, y, x, z)
    end
    ()
end

JonasIsensee avatar Jul 07 '20 08:07 JonasIsensee

How would you decide on the width of these lines?

isentropic avatar Jul 07 '20 08:07 isentropic

Try commenting out https://github.com/JuliaPlots/Plots.jl/blob/709a8a96093b512693b8703d195b5a5da0636a13/src/backends/pyplot.jl#L128 and seeing the results. Perhaps that would be exactly what you want

isentropic avatar Jul 07 '20 08:07 isentropic

If marker is unsupported by a backend, then it is provided by Plots using lower level recipes. In that case using msw, ms, lw, markercolor, linecolor can give you exactly what you want.

isentropic avatar Jul 07 '20 08:07 isentropic

One reason to have it as it is right now is that we can start allowing custom marker shapes for errorbar plots fairly easy. But I could not figure out a way to invert those markers easily for bottom/top errorbar. Caps being markers (instead of linse) can give good customizability I think

isentropic avatar Jul 07 '20 08:07 isentropic

thank you, for your suggestions. I tried redefining the above mentioned function in my script but I can't seem to figure out where _marker is defined.

JonasIsensee avatar Jul 07 '20 09:07 JonasIsensee

128 line in pyplot.jl

isentropic avatar Jul 07 '20 16:07 isentropic

Please let me know your feedback with this

isentropic avatar Jul 08 '20 01:07 isentropic

Thank you for your help.

128 line in pyplot.jl this was a misunderstanding. I found that bit. I was just wondering whether it's possible to redefine this function outside of plots and instead in my plot-script so that I don't have to rely on my custom branch of plots.jl (when executing on another machine or so)

Here's my result: As promised the thickness can now be varied but for some reason the caps are rounded on one side and not on the other.

test

JonasIsensee avatar Jul 08 '20 08:07 JonasIsensee

What about using m=:circle

isentropic avatar Oct 05 '20 03:10 isentropic

I think pyplot got better at marker centering at the latest version

isentropic avatar Oct 05 '20 03:10 isentropic

so - is there a way to remove error bar caps without removing the error bars in GR?

yanivabir avatar Mar 22 '21 14:03 yanivabir

so - is there a way to remove error bar caps without removing the error bars in GR?

With https://github.com/JuliaPlots/Plots.jl/pull/4362 that would be

using Plots
plot(1:5, yerror=fill(0.2,5), markershape = :none)

BeastyBlacksmith avatar Sep 21 '22 08:09 BeastyBlacksmith

I cannot remove error bar caps with markershape = :none i.e. running

using Plots
plot(1:5, yerror=fill(0.2,5), markershape = :none)

gives me this

截圖 2023-01-31 上午1 51 07

On the other hand, running

function errorcap_coords(errorbar, errordata, otherdata; capsize)
    ed = Vector{Plots.float_extended_type(errordata)}(undef, 0)
    od = Vector{Plots.float_extended_type(otherdata)}(undef, 0)#[Vector{float_extended_type(odi)}(undef, 0) for odi in otherdata]
    for (j, (edi, odj)) in enumerate(zip(errordata, otherdata))
        #for (i, edi) in enumerate(errordata)
            #odi = _cycle(odj, i)
            e1, e2 = Plots.error_tuple(Plots._cycle(errorbar, j))
            Plots.nanappend!(ed, [edi - e1, edi - e1])
            Plots.nanappend!(ed, [edi + e2, edi + e2])
            Plots.nanappend!(od, [odj-capsize/2, odj+capsize/2])
            Plots.nanappend!(od, [odj-capsize/2, odj+capsize/2])
        #end
    end
    return (ed, od)
end

@recipe function f(::Type{Val{:yerror}}, x, y, z)
    Plots.error_style!(plotattributes)
    #markershape := :hline
    yerr = Plots.error_zipit(plotattributes[:yerror])
    if z === nothing
        plotattributes[:y], plotattributes[:x] = Plots.error_coords(yerr, y, x)
        errcapy, errcapx = errorcap_coords(yerr, y, x; capsize=get(plotattributes, :capsize, 0.1))
        Plots.nanappend!(plotattributes[:y], errcapy)
        Plots.nanappend!(plotattributes[:x], errcapx)
    else
        plotattributes[:y], plotattributes[:x], plotattributes[:z] =
        Plots.error_coords(yerr, y, x, z)
    end
    ()
end

plot(1:5, yerror=fill(0.2,5), capsize = 0)

gives me this

截圖 2023-01-31 上午1 51 54

Yuan-Ru-Lin avatar Jan 30 '23 17:01 Yuan-Ru-Lin