Port part of errorfill from Tony Yu's mpltools. #1010

Merged
merged 8 commits into from Aug 23, 2012

4 participants

@dmcdougall
Matplotlib Developers member

Specifically, the function extrema_from_error_input from

https://github.com/tonysyu/mpltools/blob/master/mpltools/special/errorfill.py#L54

is ported to matplotlib.mlab.

@pelson pelson commented on an outdated diff Jul 14, 2012
lib/matplotlib/mlab.py
+ """
+ Offsets an array *x* by +/- an error and returns a tuple (x - err, x + err).
+
+ The error term can be:
+
+ o A scalar. In this case, the returned tuple is obvious.
+ o A vector of the same length as *x*. The quantities x +/- err are computed
+ component-wise.
+ o A tuple of length 2. In this case, xerr[0] is the error below *x* and
+ xerr[1] is error above *x*.
+ """
+ if np.isscalar(xerr) or len(xerr) == len(x):
+ xmin = x - xerr
+ xmax = x + xerr
+ elif len(xerr) == 2:
+ xmin, xmax = x - xerr[0], x + xerr[1]
@pelson
Matplotlib Developers member
pelson added a line comment Jul 14, 2012

What about the undefined case (not a scalar, nor len in [2, n])?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jdh2358 jdh2358 commented on an outdated diff Jul 14, 2012
lib/matplotlib/mlab.py
@@ -3182,3 +3182,22 @@ def quad2cubic(q0x, q0y, q1x, q1y, q2x, q2y):
c2x, c2y = c1x + 1./3. * (q2x - q0x), c1y + 1./3. * (q2y - q0y)
# c3x, c3y = q2x, q2y
return q0x, q0y, c1x, c1y, c2x, c2y, q2x, q2y
+
+def offset_line(x, xerr):
+ """
@jdh2358
jdh2358 added a line comment Jul 14, 2012

Although it is just a variable name in this context, the use case from Tony's errorfill is offsetting in the y-axis direction. Thus naming everything x and xerr might be a little confusing. Perhaps y and yerr would be better?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jdh2358 jdh2358 commented on an outdated diff Jul 14, 2012
lib/matplotlib/mlab.py
@@ -3182,3 +3182,22 @@ def quad2cubic(q0x, q0y, q1x, q1y, q2x, q2y):
c2x, c2y = c1x + 1./3. * (q2x - q0x), c1y + 1./3. * (q2y - q0y)
# c3x, c3y = q2x, q2y
return q0x, q0y, c1x, c1y, c2x, c2y, q2x, q2y
+
+def offset_line(x, xerr):
+ """
+ Offsets an array *x* by +/- an error and returns a tuple (x - err, x + err).
+
+ The error term can be:
+
+ o A scalar. In this case, the returned tuple is obvious.
+ o A vector of the same length as *x*. The quantities x +/- err are computed
+ component-wise.
+ o A tuple of length 2. In this case, xerr[0] is the error below *x* and
+ xerr[1] is error above *x*.
+ """
@jdh2358
jdh2358 added a line comment Jul 14, 2012

I suggest adding in the docstring some example code or a embedded plot directive link to an example that shows how to use this with fill_between

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@jdh2358 jdh2358 commented on an outdated diff Jul 14, 2012
lib/matplotlib/mlab.py
@@ -3182,3 +3182,22 @@ def quad2cubic(q0x, q0y, q1x, q1y, q2x, q2y):
c2x, c2y = c1x + 1./3. * (q2x - q0x), c1y + 1./3. * (q2y - q0y)
# c3x, c3y = q2x, q2y
return q0x, q0y, c1x, c1y, c2x, c2y, q2x, q2y
+
+def offset_line(x, xerr):
+ """
+ Offsets an array *x* by +/- an error and returns a tuple (x - err, x + err).
+
+ The error term can be:
+
+ o A scalar. In this case, the returned tuple is obvious.
+ o A vector of the same length as *x*. The quantities x +/- err are computed
+ component-wise.
+ o A tuple of length 2. In this case, xerr[0] is the error below *x* and
+ xerr[1] is error above *x*.
+ """
+ if np.isscalar(xerr) or len(xerr) == len(x):
@jdh2358
jdh2358 added a line comment Jul 14, 2012

Rather than np.isscalar you should use mpl's duck-typing functions, like:

if cbook.is_numlike(xerr) or (cbook.iterable(xerr) and len(xerr)==len(x)):
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@pelson
Matplotlib Developers member

Coming at this fresh, why is this called offset_line? It doesn't seem to offset anything, nor use lines. Naming functions is always the hardest part, and the best that I could come up with was error_range (which I don't actually find any better ;-) ).

I could see this function being useful for handling numpy arrays, which makes me wonder whether it would be better to put this straight into numpy rather than mlab?

@dmcdougall
Matplotlib Developers member

@pelson I have no idea! It seemed natural given the example in the docstring. How about offset_data?

@jdh2358

@pelson re numpy vs mlab: if we have something that is useful to people I like to ship it so that our code works across multiple versions of numpy. If numpy wants something from mlab, or someone wants to push to get it into numpy, that's great, but I'd like to have it in several release cycles of numpy before we stop shipping it. We've done this with a ton of mlab functions over the years and it doesn't seem to have caused many problems. This function in particular is mainly a convenience method for fill_between -- I don't see it being generically useful enough to make it into numpy.

@pelson pelson commented on an outdated diff Jul 15, 2012
lib/matplotlib/mlab.py
@@ -3182,3 +3182,34 @@ def quad2cubic(q0x, q0y, q1x, q1y, q2x, q2y):
c2x, c2y = c1x + 1./3. * (q2x - q0x), c1y + 1./3. * (q2y - q0y)
# c3x, c3y = q2x, q2y
return q0x, q0y, c1x, c1y, c2x, c2y, q2x, q2y
+
+def offset_line(y, yerr):
+ """
+ Offsets an array *y* by +/- an error and returns a tuple (y - err, y + err).
+
+ The error term can be:
+
+ o A scalar. In this case, the returned tuple is obvious.
+ o A vector of the same length as *y*. The quantities y +/- err are computed
+ component-wise.
+ o A tuple of length 2. In this case, yerr[0] is the error below *y* and
+ yerr[1] is error above *y*.
+
+ For example:
@pelson
Matplotlib Developers member
pelson added a line comment Jul 15, 2012

My memory is a little haze, but if you put a double colon, and then indent the code, you will get a pretty code block if/when rendered with Sphinx.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@WeatherGod WeatherGod and 2 others commented on an outdated diff Jul 21, 2012
lib/matplotlib/mlab.py
@@ -3182,3 +3182,34 @@ def quad2cubic(q0x, q0y, q1x, q1y, q2x, q2y):
c2x, c2y = c1x + 1./3. * (q2x - q0x), c1y + 1./3. * (q2y - q0y)
# c3x, c3y = q2x, q2y
return q0x, q0y, c1x, c1y, c2x, c2y, q2x, q2y
+
+def offset_line(y, yerr):
+ """
+ Offsets an array *y* by +/- an error and returns a tuple (y - err, y + err).
+
+ The error term can be:
+
+ o A scalar. In this case, the returned tuple is obvious.
+ o A vector of the same length as *y*. The quantities y +/- err are computed
+ component-wise.
+ o A tuple of length 2. In this case, yerr[0] is the error below *y* and
+ yerr[1] is error above *y*.
+
+ .. For example::
@WeatherGod
Matplotlib Developers member
WeatherGod added a line comment Jul 21, 2012

remove "For"

@pelson
Matplotlib Developers member
pelson added a line comment Jul 21, 2012

And the leading .. ?

@WeatherGod
Matplotlib Developers member
WeatherGod added a line comment Jul 21, 2012

I keep getting sphinx syntax mixed up in my head. A directive is made up of a leading double dots, a space and a single word followed by double colons. Any arguments for that directive follow the double colons. "keyword" arguments (if any) are provided on the subsequent indented lines with the name of the keyword surrounded by colons and the value after it (see doc/api/index.rst for an example). Finally, any remaining indented text is treated as-is as a large string block.

It doesn't look like there is an ".. example::" directive, so I would re-write this part of the docstring as:

yerr[1] is error above *y*. For example::
@dmcdougall
Matplotlib Developers member
dmcdougall added a line comment Jul 21, 2012

@WeatherGod Where do I put the double dots in your example?

I followed the example below (from pyplot.py):

def figlegend(handles, labels, loc, **kwargs):
    """
    Place a legend in the figure.

    *labels*
      a sequence of strings

    *handles*
      a sequence of :class:`~matplotlib.lines.Line2D` or
      :class:`~matplotlib.patches.Patch` instances

    *loc*
      can be a string or an integer specifying the legend
      location

    A :class:`matplotlib.legend.Legend` instance is returned.

    Example::

      figlegend( (line1, line2, line3),
                 ('label1', 'label2', 'label3'),
                 'upper right' )

    .. seealso::

       :func:`~matplotlib.pyplot.legend`

    """
    l = gcf().legend(handles, labels, loc, **kwargs)
    draw_if_interactive()
    return l

I don't have sphinx, and I have no experience with it, so I'm deferring to you guys for epic sphinxage :)

@WeatherGod
Matplotlib Developers member
WeatherGod added a line comment Jul 22, 2012

You don't. Whenever you want to display a code block, you use just double-colons and indent the code block. You might also have to have a blank line as well. The word "example" before the double-colons is not special and can be any word. Sometimes, we even just use double colons on a line all by itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@pelson pelson commented on an outdated diff Jul 21, 2012
lib/matplotlib/mlab.py
+ o A scalar. In this case, the returned tuple is obvious.
+ o A vector of the same length as *y*. The quantities y +/- err are computed
+ component-wise.
+ o A tuple of length 2. In this case, yerr[0] is the error below *y* and
+ yerr[1] is error above *y*.
+
+ .. For example::
+
+ from pylab import *
+ x = linspace(0, 2*pi, num=100, endpoint=True)
+ y = sin(x)
+ y_minus, y_plus = mlab.offset_line(y, 0.1)
+ plot(x, y)
+ fill_between(x, ym, y2=yp)
+ show()
+ """
@pelson
Matplotlib Developers member
pelson added a line comment Jul 21, 2012

I think there is supposed to be an empty line after the end of the code-block. Stylistically it doesn't hurt either.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@pelson
Matplotlib Developers member

Ok, there is no reason for this not to make it into 1.2.x so I have updated the milestone.

@mdboom
Matplotlib Developers member

@dmcdougall : Do you consider this ready to merge?

@dmcdougall
Matplotlib Developers member

@mdboom Sounds good to me. Thanks for the feedback.

@mdboom mdboom merged commit 486a0cd into matplotlib:master Aug 23, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment