Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Added ability to offset errorbars when using errorevery. #6280

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

Merged
merged 11 commits into from
Apr 28, 2019

Conversation

jcalbert
Copy link
Contributor

@jcalbert jcalbert commented Apr 8, 2016

I was having a problem where errorbars for different series would perfectly overlap, as shown in the top plot:

compare

I added the ability to specify the errorevery such that

plt.errorbar(x,y,yerr, errorevery=7) still adds error bars to the 1st, 8th, 15th ... data points but

plt.errorbar(x,y,yerr, errorevery=(7,3)) adds error bars to the 3rd, 10th, 17th ... points.

I apologize if I'm violating some protocol - I haven't contributed to a project like this before.


P.S. The plot above was generated by:

from matplotlib import pyplot as plt
import numpy as np

x1 = np.arange(0,1000.)
x2 = np.arange(0,1000.)

y1 = np.cumsum(np.random.rand(x1.size)-.495)
y2 = np.cumsum(np.random.rand(x2.size)-.505)

ye1 = x1**.5 / 4
ye2 = x2**.5 / 4

dat1 = [x1,y1,ye1]
dat2 = [x2,y2,ye2]


fig = plt.figure(figsize=(12,8))

ax_before = fig.add_subplot(2,1,1); ax_before.set_title('Without offsets')
ax_after  = fig.add_subplot(2,1,2); ax_after.set_title('With offsets')

for x,y,ye in [dat1, dat2]:
    shift = np.all(y==y1) * 20

    ax_before.errorbar(x,y,ye, errorevery=40,capsize=5)
    ax_after.errorbar(x,y,ye, errorevery=(40,shift),capsize=5)

fig.savefig('compare.png')

@tacaswell tacaswell added this to the 2.1 (next point release) milestone Apr 8, 2016
if not iterable(errorevery):
errorevery = (errorevery, 0)
try:
assert all(int(x)==x for x in errorevery)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I while ago we cleaned all of the asserts out of the main code base and use a if ... raise pattern.

@tacaswell
Copy link
Member

👍 I like the idea, just left some code-style comments.

Can you also add a test (just a png image comparison should be enough) and a whats_new entry ( https://github.com/matplotlib/matplotlib/tree/master/doc/users/whats_new )

@jcalbert
Copy link
Contributor Author

jcalbert commented Apr 8, 2016

Done - is there any documentation / examples I should do?

@tacaswell
Copy link
Member

👍 Looks good to me.

If you want to update the mark-every examples in the gallery that would be good.

subsamples the errorbars. e.g., if errorevery=5, errorbars for
every 5-th datapoint will be plotted. The data plot itself still
shows all data points.
every 5-th datapoint will be plotted. If errorevery=(a,b), points
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space after comma in tuple.

@jcalbert
Copy link
Contributor Author

jcalbert commented Apr 9, 2016

In an earlier revision, I had validation of the sort which ensured

  1. errorevery==int(errorevery)
  2. offset==int(offset)
  3. errorevery>=1
  4. offset>=0
    but simplified it in accordance with @tacaswell 's suggestion above.

....

Regarding @QuLogic 's note:

Personally, I prefer
(np.arange(N) + offset) % errorevery == 0
over

offset %= errorevery 
np.arange(N) % errorevery == offset

@tacaswell
Copy link
Member

Sometimes I make bad suggestions 🐑

(np.arange(N) + offset) % errorevery == 0 seems like a nice way to do this.

@QuLogic
Copy link
Member

QuLogic commented Apr 11, 2016

That should be - offset.

@jcalbert
Copy link
Contributor Author

Hm, not sure how I broke test_savefig_to_stringio_eps_afm.

@jcalbert
Copy link
Contributor Author

(My bad, I didn't realize "Close and comment" closed the issue)

@tacaswell
Copy link
Member

That test is known to be flaky, but no one has been able to reproduce it reliably to debug it. We are pretty sure it is an artifact of running nose in parallel + not perfectly isolated tests.

@dstansby
Copy link
Member

dstansby commented Feb 6, 2017

This looks like a cool new feature! @jcalbert could you rebase this on to the current master branch, and then I'll have a look at it in detail?

@jcalbert
Copy link
Contributor Author

jcalbert commented Feb 7, 2017

@dstansby Alright, it's going to take me a second to learn git rebase though, since I haven't used it before.

@tacaswell
Copy link
Member

Sorry this got lost in the weeds 😞

@dstansby dstansby self-assigned this Feb 8, 2017
@tacaswell tacaswell reopened this Feb 24, 2019
@tacaswell tacaswell modified the milestones: v3.1.0, v3.2.0 Feb 24, 2019
@tacaswell
Copy link
Member

power-cycled to re-run CI.

This looks like a very useful feature (that got a lot of discussion), however from skimming the discussion it looks like there are still outstanding issues with the API being inconsistent with the API of plot?

@jcalbert
Copy link
Contributor Author

IIRC, I think it was getting mysteriously snagged on one of the CI tests but I don't know how to go back and check old results. I think we settled on having errorevery support a subset of the functionality of markevery, but that subset is consistent.

I'll rebase and fix one style issue "(6,3)" -> "(6, 3)" tomorrow

@jcalbert
Copy link
Contributor Author

Should be good now.

@tacaswell tacaswell added the Release critical For bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions. label Apr 20, 2019
@@ -0,0 +1,10 @@
Errorbar plots can shift which points have error bars
----------------------
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decorator should be the same length as the title.

Copy link
Member

@tacaswell tacaswell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modulo moving the whats_new file to next_whats_new

@tacaswell
Copy link
Member

Thanks for sticking with this for so long!



# Now switch to a more OO interface to exercise more features.
fig, axs = plt.subplots(nrows=1, ncols=2, sharex=True)
fig, axs = plt.subplots(nrows=1, ncols=3, sharex=True, figsize=(12, 6))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fig, axs = plt.subplots(nrows=1, ncols=3, sharex=True, figsize=(12, 6))
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=3, sharex=True, figsize=(12, 6))

and using ax1, ax2 below is better than reassigning ax = ax[i] later.

@@ -12,22 +12,29 @@

# example data
x = np.arange(0.1, 4, 0.1)
y = np.exp(-x)
y = np.exp(np.vstack([-1.0 * x, -.5 * x]))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, it's easier to follow if you keep the data in two separate variables:

y1 = np.exp(-x)
y2 = np.exp(-0.5 * x)


# example variable error bar values
yerr = 0.1 + 0.1 * np.sqrt(x)
yerr = 0.1 + 0.1 * np.sqrt(np.vstack([x, x/2]))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with y1err, y2err.

ax = axs[0]
ax.errorbar(x, y, yerr=yerr)
for i in range(2):
ax.errorbar(x, y[i], yerr=yerr[i])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With above changes simpler:

ax1.errorbar(x, y1, yerr=y1err)
ax1.errorbar(x, y2, yerr=y2err)

Same for the following for loops.

except TypeError:
offset = 0

int_msg = 'errorevery must be positive integer or tuple of integers'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's more readable to define the message directly in the context of the ValueError. Also, this allows to make the error message more specific.

        if errorevery < 1 or int(errorevery) != errorevery:
            raise ValueError(
                'errorevery must be positive integer or tuple of integers')
        if int(offset) != offset:
            raise ValueError('errorevery offset must be an integer >= 0')

@jcalbert
Copy link
Contributor Author

Added @timhoffm 's suggestions, fixed what's new.

@timhoffm
Copy link
Member

timhoffm commented Apr 26, 2019

flake8 complains:
./examples/lines_bars_and_markers/errorbar_subsample.py:24:57: W291 trailing whitespace

Doc build is broken due to #14003 (unrelated to this PR). Can be fixed by rebasing.

@timhoffm timhoffm merged commit 4507a59 into matplotlib:master Apr 28, 2019
@timhoffm
Copy link
Member

Thanks a lot for keeping at this! I think this is PR with the longest history I've merged into matplotlib. Anyway, glad that it's in 🎉. Hope to see you back some time.

@jcalbert jcalbert deleted the errorbar-offset branch May 3, 2019 23:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Release critical For bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants