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

update errorbar artists #4556

Closed
zhangt58 opened this issue Jun 26, 2015 · 6 comments
Closed

update errorbar artists #4556

zhangt58 opened this issue Jun 26, 2015 · 6 comments
Labels
Difficulty: Medium https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues
Milestone

Comments

@zhangt58
Copy link

Hi,
I'm try to make the matplotlib errorbar plotting update as some input data varies, such plotting is embed into wxpython panel. I can update the matplotlib.line2D container field properties, e.g. update x_data through set_xdata, etc, but I cannot find any clue about how to control the Errorbar container's properties, can somebody help me out?
Thanks!
Tong Zhang

@tacaswell
Copy link
Member

See http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.errorbar

errorbar returns a funny object which sort of looks like an Artist, but is actually a subclass of a tuple, that plays nice with the legend.

For now, you just have to go in and poke at each of the pieces individually, but these really should be methods on the object returned by errorbar, ex the following will change the errorbars symmetrically

import numpy as np
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
x = np.linspace(0, 2*np.pi, 15)
y = np.sin(x)
y_err = np.cos(x) * 0.1
err = ax.errorbar(x, y, y_err)


def adjust_yerr(err, y_error):
    ln, (err_top, err_bot), (bars, ) = err
    y_base = ln.get_ydata()
    x_base = ln.get_xdata()

    yerr_top = y_base + y_error
    yerr_bot = y_base - y_error

    err_top.set_ydata(yerr_top)
    err_bot.set_ydata(yerr_bot)

    new_segments = [np.array([[x, yt], [x, yb]]) for
                    x, yt, yb in zip(x_base, yerr_top, yerr_bot)]

    bars.set_segments(new_segments)
    plt.draw() # don't need this on master

I started a bit of work to clean this up (#3944) but have not had time to come back to that in a while.

@tacaswell tacaswell added this to the proposed next point release milestone Jun 26, 2015
@tacaswell tacaswell changed the title update matplotlib errorbar plotting in wxPython frame update errorbar artists Jun 26, 2015
@tacaswell tacaswell added the Difficulty: Medium https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues label Jun 26, 2015
@tacaswell
Copy link
Member

I changed the title as this has nothing to do with Wx

@zhangt58
Copy link
Author

@tacaswell thanks very much for your script, now I got my job done. Below is the script that both update xerr and yerr, as well as the x_data and y_data for the baseline plotting:

def adjustErrbarxy(errobj, x, y, x_error, y_error):
    ln, (errx_top, errx_bot, erry_top, erry_bot), (barsx, barsy) = errobj

    ln.set_data(x,y)
    x_base = x
    y_base = y

    xerr_top = x_base + x_error
    xerr_bot = x_base - x_error
    yerr_top = y_base + y_error
    yerr_bot = y_base - y_error

    errx_top.set_xdata(xerr_top)
    errx_bot.set_xdata(xerr_bot)
    errx_top.set_ydata(y_base)
    errx_bot.set_ydata(y_base)

    erry_top.set_xdata(x_base)
    erry_bot.set_xdata(x_base)
    erry_top.set_ydata(yerr_top)
    erry_bot.set_ydata(yerr_bot)

    new_segments_x = [np.array([[xt, y], [xb,y]]) for xt, xb, y in zip(xerr_top, xerr_bot, y_base)]
    new_segments_y = [np.array([[x, yt], [x,yb]]) for x, yt, yb in zip(x_base, yerr_top, yerr_bot)]
    barsx.set_segments(new_segments_x)
    barsy.set_segments(new_segments_y)

In which x, y, x_error, y_error are the input parameters (dynamic numpy arrays) for updating errorbar artists; however, another issue was discovered, that is the updated plotting just show scattered point, but not line up the x-y plot, I fixed it by drawing one more x-y line2D artist and then update its x_data and y_data; I'm wandering if there is an easy way to show the x-y line2D plot when I update the errorbar artists?

@tacaswell
Copy link
Member

Great, closing as not really a bug and #3944 is heading towards cleaning up bundled artists.

You just need to add ln.set_data(x, y) to update the line in your function (or I don't understand your question).

@archman If you wouldn't mind cleaning that function up a bit so that it does not take self and deals with not having all of the artists it expects that would make a good addition to the examples.

@zhangt58
Copy link
Author

zhangt58 commented Jul 2, 2015

@tacaswell update the post as suggested, thanks.

@mitpre
Copy link

mitpre commented Jan 10, 2020

Just leaving here a bit more general solution. It still has a flaw that you can swap the directions of errorbars when updating ...

def update_errorbar(errobj, x, y, xerr=None, yerr=None):
    ln, caps, bars = errobj


    if len(bars) == 2:
        assert xerr is not None and yerr is not None, "Your errorbar object has 2 dimension of error bars defined. You must provide xerr and yerr."
        barsx, barsy = bars  # bars always exist (?)
        try:  # caps are optional
            errx_top, errx_bot, erry_top, erry_bot = caps
        except ValueError:  # in case there is no caps
            pass

    elif len(bars) == 1:
        assert (xerr is     None and yerr is not None) or\
               (xerr is not None and yerr is     None),  \
               "Your errorbar object has 1 dimension of error bars defined. You must provide xerr or yerr."

        if xerr is not None:
            barsx, = bars  # bars always exist (?)
            try:
                errx_top, errx_bot = caps
            except ValueError:  # in case there is no caps
                pass
        else:
            barsy, = bars  # bars always exist (?)
            try:
                erry_top, erry_bot = caps
            except ValueError:  # in case there is no caps
                pass

    ln.set_data(x,y)

    try:
        errx_top.set_xdata(x + xerr)
        errx_bot.set_xdata(x - xerr)
        errx_top.set_ydata(y)
        errx_bot.set_ydata(y)
    except NameError:
        pass
    try:
        barsx.set_segments([np.array([[xt, y], [xb, y]]) for xt, xb, y in zip(x + xerr, x - xerr, y)])
    except NameError:
        pass

    try:
        erry_top.set_xdata(x)
        erry_bot.set_xdata(x)
        erry_top.set_ydata(y + yerr)
        erry_bot.set_ydata(y - yerr)
    except NameError:
        pass
    try:
        barsy.set_segments([np.array([[x, yt], [x, yb]]) for x, yt, yb in zip(x, y + yerr, y - yerr)])
    except NameError:
        pass

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Difficulty: Medium https://matplotlib.org/devdocs/devel/contribute.html#good-first-issues
Projects
None yet
Development

No branches or pull requests

3 participants