Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customize errorbars (in particular their caps) #2156

Open
Krastanov opened this issue Aug 22, 2019 · 26 comments
Open

Customize errorbars (in particular their caps) #2156

Krastanov opened this issue Aug 22, 2019 · 26 comments
Labels
enhancement improving existing functionality

Comments

@Krastanov
Copy link

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.

@mkborregaard
Copy link
Member

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

@Krastanov
Copy link
Author

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

@mmikhasenko
Copy link
Contributor

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.

@BeastyBlacksmith BeastyBlacksmith added the enhancement improving existing functionality label Apr 28, 2020
@isentropic
Copy link
Member

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

@isentropic
Copy link
Member

@Krastanov

@JonasIsensee
Copy link
Contributor

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

@isentropic
Copy link
Member

I think lw, msw or some other thing does it

@JonasIsensee
Copy link
Contributor

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

@isentropic
Copy link
Member

What backend are you using

@isentropic
Copy link
Member

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

@JonasIsensee
Copy link
Contributor

pyplot

@JonasIsensee
Copy link
Contributor

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 #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

@isentropic
Copy link
Member

How would you decide on the width of these lines?

@isentropic
Copy link
Member

Try commenting out

marker == :hline && return "_"
and seeing the results. Perhaps that would be exactly what you want

@isentropic
Copy link
Member

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
Copy link
Member

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

@JonasIsensee
Copy link
Contributor

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.

@isentropic
Copy link
Member

128 line in pyplot.jl

@isentropic
Copy link
Member

Please let me know your feedback with this

@JonasIsensee
Copy link
Contributor

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

@isentropic
Copy link
Member

What about using m=:circle

@isentropic
Copy link
Member

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

@yanivabir
Copy link

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

@BeastyBlacksmith
Copy link
Member

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

With #4362 that would be

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

@Yuan-Ru-Lin
Copy link

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

@TimoLautenschlager
Copy link

i implemented the function mentioned here, but when i try to use for example "capsize = 5", i get following error

! Package pgfkeys Error: I do not know the key '/tikz/capsize', to which you pa
ssed '5', and I am going to ignore it. Perhaps you misspelled it.

(even with the example used in this thread: plot(1:5, yerror=fill(0.2,5), capsize = 0))

Could it have anything to do with me using PGFPlotsX? or did i do something wrong

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement improving existing functionality
Projects
None yet
Development

No branches or pull requests

9 participants