From d1147bf5db7781cb58b6f1ab82a57551714a0312 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 11 May 2015 17:13:04 -0400 Subject: [PATCH 1/9] DOC : first pass at numpydoc-ifying fill_between Changes to docstring to attempt to conform to numpydoc format. --- lib/matplotlib/axes/_axes.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index eb941666832c..b5cec374b635 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -4440,37 +4440,46 @@ def fill_between(self, x, y1, y2=0, where=None, interpolate=False, filling the regions between *y1* and *y2* where ``where==True`` - *x* : + Parameters + ---------- + x : array An N-length array of the x data - *y1* : + y1 : array An N-length array (or scalar) of the y data - *y2* : + y2 : array An N-length array (or scalar) of the y data - *where* : - If *None*, default to fill between everywhere. If not *None*, + where : array, optional + If `None`, default to fill between everywhere. If not `None`, it is an N-length numpy boolean array and the fill will only happen over the regions where ``where==True``. - *interpolate* : - If *True*, interpolate between the two lines to find the + interpolate : bool, optional + If `True`, interpolate between the two lines to find the precise point of intersection. Otherwise, the start and end points of the filled region will only occur on explicit values in the *x* array. - *kwargs* : - Keyword args passed on to the - :class:`~matplotlib.collections.PolyCollection`. + + Note + ---- + + Additional Keyword args passed on to the + :class:`~matplotlib.collections.PolyCollection`. kwargs control the :class:`~matplotlib.patches.Polygon` properties: %(PolyCollection)s + Examples + -------- + .. plot:: mpl_examples/pylab_examples/fill_between_demo.py - .. seealso:: + See Also + -------- :meth:`fill_betweenx` for filling between two sets of x-values From 67b8c0c08ab1ebd0bb7fffe0975d481735349706 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 15 May 2015 15:15:00 -0400 Subject: [PATCH 2/9] MNT : centralize pt -> step logic Added 3 public functions for converting points -> steps for the 3 step locations + 1 private function for input validation --- lib/matplotlib/cbook.py | 146 +++++++++++++++++++++++++++++ lib/matplotlib/tests/test_cbook.py | 74 ++++++++++++++- 2 files changed, 219 insertions(+), 1 deletion(-) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 47ba95ad994c..91f89a2c1099 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2406,6 +2406,152 @@ def get_instancemethod(self): return getattr(self.parent_obj, self.instancemethod_name) +def _step_validation(x, *args): + """ + Helper function of `pts_to_*step` functions + + This function does all of the normalization required to the + input and generate the template for output + + + """ + args = tuple(np.asanyarray(y) for y in args) + x = np.asanyarray(x) + if x.ndim != 1: + raise ValueError("x must be 1 dimenional") + if len(args) == 0: + raise ValueError("At least one Y value must be passed") + + return np.vstack((x, ) + args) + + +def pts_to_prestep(x, *args): + """ + Covert continuous line to pre-steps + + Given a set of N points convert to 2 N -1 points + which when connected linearly give a step function + which changes values at the begining the intervals. + + Parameters + ---------- + x : array + The x location of the steps + + y1, y2, ... : array + Any number of y arrays to be turned into steps. + All must be the same length as ``x`` + + Returns + ------- + x, y1, y2, .. : array + The x and y values converted to steps in the same order + as the input. If the input is length ``N``, each of these arrays + will be length ``2N + 1`` + + + Example + ------- + + >> x_s, y1_s, y2_s = pts_to_prestep(x, y1, y2) + """ + # do normalization + vertices = _step_validation(x, *args) + # create the output array + steps = np.zeros((vertices.shape[0], 2 * len(x) - 1), np.float) + # do the to step conversion logic + steps[0, 0::2], steps[0, 1::2] = vertices[0, :], vertices[0, :-1] + steps[1:, 0::2], steps[1:, 1:-1:2] = vertices[1:, :], vertices[1:, 1:] + # convert 2D array back to tuple + return tuple(steps) + + +def pts_to_poststep(x, *args): + """ + Covert continuous line to pre-steps + + Given a set of N points convert to 2 N -1 points + which when connected linearly give a step function + which changes values at the begining the intervals. + + Parameters + ---------- + x : array + The x location of the steps + + y1, y2, ... : array + Any number of y arrays to be turned into steps. + All must be the same length as ``x`` + + Returns + ------- + x, y1, y2, .. : array + The x and y values converted to steps in the same order + as the input. If the input is length ``N``, each of these arrays + will be length ``2N + 1`` + + + Example + ------- + + >> x_s, y1_s, y2_s = pts_to_prestep(x, y1, y2) + """ + # do normalization + vertices = _step_validation(x, *args) + # create the output array + steps = ma.zeros((vertices.shape[0], 2 * len(x) - 1), np.float) + # do the to step conversion logic + steps[0, ::2], steps[0, 1:-1:2] = vertices[0, :], vertices[0, 1:] + steps[1:, 0::2], steps[1:, 1::2] = vertices[1:, :], vertices[1:, :-1] + + # convert 2D array back to tuple + return tuple(steps) + + +def pts_to_midstep(x, *args): + """ + Covert continuous line to pre-steps + + Given a set of N points convert to 2 N -1 points + which when connected linearly give a step function + which changes values at the begining the intervals. + + Parameters + ---------- + x : array + The x location of the steps + + y1, y2, ... : array + Any number of y arrays to be turned into steps. + All must be the same length as ``x`` + + Returns + ------- + x, y1, y2, .. : array + The x and y values converted to steps in the same order + as the input. If the input is length ``N``, each of these arrays + will be length ``2N + 1`` + + + Example + ------- + + >> x_s, y1_s, y2_s = pts_to_prestep(x, y1, y2) + """ + # do normalization + vertices = _step_validation(x, *args) + # create the output array + steps = ma.zeros((vertices.shape[0], 2 * len(x)), np.float) + steps[0, 1:-1:2] = 0.5 * (vertices[0, :-1] + vertices[0, 1:]) + steps[0, 2::2] = 0.5 * (vertices[0, :-1] + vertices[0, 1:]) + steps[0, 0] = vertices[0, 0] + steps[0, -1] = vertices[0, -1] + steps[1:, 0::2], steps[1:, 1::2] = vertices[1:, :], vertices[1:, :] + + # convert 2D array back to tuple + return tuple(steps) + + # Numpy > 1.6.x deprecates putmask in favor of the new copyto. # So long as we support versions 1.6.x and less, we need the # following local version of putmask. We choose to make a diff --git a/lib/matplotlib/tests/test_cbook.py b/lib/matplotlib/tests/test_cbook.py index cbe24e54cb1b..2b916b08566f 100644 --- a/lib/matplotlib/tests/test_cbook.py +++ b/lib/matplotlib/tests/test_cbook.py @@ -8,7 +8,8 @@ import numpy as np from numpy.testing.utils import (assert_array_equal, assert_approx_equal, assert_array_almost_equal) -from nose.tools import assert_equal, assert_not_equal, raises, assert_true +from nose.tools import (assert_equal, assert_not_equal, raises, assert_true, + assert_raises) import matplotlib.cbook as cbook import matplotlib.colors as mcolors @@ -304,3 +305,74 @@ def test_callback_complete(self): def dummy(self): pass + + +def test_to_prestep(): + x = np.arange(4) + y1 = np.arange(4) + y2 = np.arange(4)[::-1] + + xs, y1s, y2s = cbook.pts_to_prestep(x, y1, y2) + + x_target = np.asarray([0, 0, 1, 1, 2, 2, 3], dtype='float') + y1_target = np.asarray([0, 1, 1, 2, 2, 3, 3], dtype='float') + y2_target = np.asarray([3, 2, 2, 1, 1, 0, 0], dtype='float') + + assert_array_equal(x_target, xs) + assert_array_equal(y1_target, y1s) + assert_array_equal(y2_target, y2s) + + xs, y1s = cbook.pts_to_prestep(x, y1) + assert_array_equal(x_target, xs) + assert_array_equal(y1_target, y1s) + + +def test_to_poststep(): + x = np.arange(4) + y1 = np.arange(4) + y2 = np.arange(4)[::-1] + + xs, y1s, y2s = cbook.pts_to_poststep(x, y1, y2) + + x_target = np.asarray([0, 1, 1, 2, 2, 3, 3], dtype='float') + y1_target = np.asarray([0, 0, 1, 1, 2, 2, 3], dtype='float') + y2_target = np.asarray([3, 3, 2, 2, 1, 1, 0], dtype='float') + + assert_array_equal(x_target, xs) + assert_array_equal(y1_target, y1s) + assert_array_equal(y2_target, y2s) + + xs, y1s = cbook.pts_to_poststep(x, y1) + assert_array_equal(x_target, xs) + assert_array_equal(y1_target, y1s) + + +def test_to_midstep(): + x = np.arange(4) + y1 = np.arange(4) + y2 = np.arange(4)[::-1] + + xs, y1s, y2s = cbook.pts_to_midstep(x, y1, y2) + + x_target = np.asarray([0, .5, .5, 1.5, 1.5, 2.5, 2.5, 3], dtype='float') + y1_target = np.asarray([0, 0, 1, 1, 2, 2, 3, 3], dtype='float') + y2_target = np.asarray([3, 3, 2, 2, 1, 1, 0, 0], dtype='float') + + assert_array_equal(x_target, xs) + assert_array_equal(y1_target, y1s) + assert_array_equal(y2_target, y2s) + + xs, y1s = cbook.pts_to_midstep(x, y1) + assert_array_equal(x_target, xs) + assert_array_equal(y1_target, y1s) + + +def test_step_fails(): + assert_raises(ValueError, cbook._step_validation, + np.arange(12).reshape(3, 4), 'a') + assert_raises(ValueError, cbook._step_validation, + np.arange(12), 'a') + assert_raises(ValueError, cbook._step_validation, + np.arange(12)) + assert_raises(ValueError, cbook._step_validation, + np.arange(12), np.arange(3)) From 92a90bfdc6be8adc512b3dfc7c6696f286c94a97 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 15 May 2015 15:53:42 -0400 Subject: [PATCH 3/9] MNT : implement steps with cbook functions Use the centralized version of the points -> step conversion. Slightly questioning my API choices on the cbook functions due to awkwardly large number of .T to make this work here. --- lib/matplotlib/lines.py | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/lib/matplotlib/lines.py b/lib/matplotlib/lines.py index 82beb95428ef..52e21b8a4b9b 100644 --- a/lib/matplotlib/lines.py +++ b/lib/matplotlib/lines.py @@ -16,7 +16,9 @@ from matplotlib import verbose from . import artist from .artist import Artist -from .cbook import iterable, is_string_like, is_numlike, ls_mapper_r +from .cbook import (iterable, is_string_like, is_numlike, ls_mapper_r, + pts_to_prestep, pts_to_poststep, pts_to_midstep) + from .colors import colorConverter from .path import Path from .transforms import Bbox, TransformedPath, IdentityTransform @@ -1154,36 +1156,21 @@ def _draw_lines(self, renderer, gc, path, trans): self._lineFunc(renderer, gc, path, trans) def _draw_steps_pre(self, renderer, gc, path, trans): - vertices = self._xy - steps = ma.zeros((2 * len(vertices) - 1, 2), np.float_) - - steps[0::2, 0], steps[1::2, 0] = vertices[:, 0], vertices[:-1, 0] - steps[0::2, 1], steps[1:-1:2, 1] = vertices[:, 1], vertices[1:, 1] + steps = np.vstack(pts_to_prestep(*self._xy.T)).T path = Path(steps) path = path.transformed(self.get_transform()) self._lineFunc(renderer, gc, path, IdentityTransform()) def _draw_steps_post(self, renderer, gc, path, trans): - vertices = self._xy - steps = ma.zeros((2 * len(vertices) - 1, 2), np.float_) - - steps[::2, 0], steps[1:-1:2, 0] = vertices[:, 0], vertices[1:, 0] - steps[0::2, 1], steps[1::2, 1] = vertices[:, 1], vertices[:-1, 1] + steps = np.vstack(pts_to_poststep(*self._xy.T)).T path = Path(steps) path = path.transformed(self.get_transform()) self._lineFunc(renderer, gc, path, IdentityTransform()) def _draw_steps_mid(self, renderer, gc, path, trans): - vertices = self._xy - steps = ma.zeros((2 * len(vertices), 2), np.float_) - - steps[1:-1:2, 0] = 0.5 * (vertices[:-1, 0] + vertices[1:, 0]) - steps[2::2, 0] = 0.5 * (vertices[:-1, 0] + vertices[1:, 0]) - steps[0, 0] = vertices[0, 0] - steps[-1, 0] = vertices[-1, 0] - steps[0::2, 1], steps[1::2, 1] = vertices[:, 1], vertices[:, 1] + steps = np.vstack(pts_to_midstep(*self._xy.T)).T path = Path(steps) path = path.transformed(self.get_transform()) From 2faead5712f851972297710762d25e934962e038 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 15 May 2015 16:15:17 -0400 Subject: [PATCH 4/9] ENH : add look-up dictionary for step functions Add dict to cbook to map strings -> functions --- lib/matplotlib/cbook.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/matplotlib/cbook.py b/lib/matplotlib/cbook.py index 91f89a2c1099..bec4002b1ec7 100644 --- a/lib/matplotlib/cbook.py +++ b/lib/matplotlib/cbook.py @@ -2551,6 +2551,12 @@ def pts_to_midstep(x, *args): # convert 2D array back to tuple return tuple(steps) +STEP_LOOKUP_MAP = {'pre': pts_to_prestep, + 'post': pts_to_poststep, + 'mid': pts_to_midstep, + 'step-pre': pts_to_prestep, + 'step-post': pts_to_poststep, + 'step-mid': pts_to_midstep} # Numpy > 1.6.x deprecates putmask in favor of the new copyto. # So long as we support versions 1.6.x and less, we need the From f8ae4ffe829311dcf80aed51f1b2b9251d3160f3 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 15 May 2015 16:18:12 -0400 Subject: [PATCH 5/9] ENH : add step_where kwarg to fill_between{x} Add ability to fill between 'step' plots. Closes #643 and #1709 --- lib/matplotlib/axes/_axes.py | 50 +++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index b5cec374b635..4ebd08d92ace 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -13,7 +13,7 @@ import matplotlib import matplotlib.cbook as cbook -from matplotlib.cbook import _string_to_bool, mplDeprecation +from matplotlib.cbook import mplDeprecation, STEP_LOOKUP_MAP import matplotlib.collections as mcoll import matplotlib.colors as mcolors import matplotlib.contour as mcontour @@ -4428,13 +4428,11 @@ def fill(self, *args, **kwargs): @docstring.dedent_interpd def fill_between(self, x, y1, y2=0, where=None, interpolate=False, + step_where=None, **kwargs): """ Make filled polygons between two curves. - Call signature:: - - fill_between(x, y1, y2=0, where=None, **kwargs) Create a :class:`~matplotlib.collections.PolyCollection` filling the regions between *y1* and *y2* where @@ -4462,9 +4460,12 @@ def fill_between(self, x, y1, y2=0, where=None, interpolate=False, end points of the filled region will only occur on explicit values in the *x* array. + step_where : {'pre', 'post', 'mid'}, optional + If not None, fill with step logic. + - Note - ---- + Notes + ----- Additional Keyword args passed on to the :class:`~matplotlib.collections.PolyCollection`. @@ -4516,6 +4517,9 @@ def fill_between(self, x, y1, y2=0, where=None, interpolate=False, xslice = x[ind0:ind1] y1slice = y1[ind0:ind1] y2slice = y2[ind0:ind1] + if step_where is not None: + step_func = STEP_LOOKUP_MAP[step_where] + xslice, y1slice, y2slice = step_func(xslice, y1slice, y2slice) if not len(xslice): continue @@ -4576,7 +4580,8 @@ def get_interp_point(ind): return collection @docstring.dedent_interpd - def fill_betweenx(self, y, x1, x2=0, where=None, **kwargs): + def fill_betweenx(self, y, x1, x2=0, where=None, + step_where=None, **kwargs): """ Make filled polygons between two horizontal curves. @@ -4588,19 +4593,27 @@ def fill_betweenx(self, y, x1, x2=0, where=None, **kwargs): filling the regions between *x1* and *x2* where ``where==True`` - *y* : + Parameters + ---------- + y : array An N-length array of the y data - *x1* : + x1 : array An N-length array (or scalar) of the x data - *x2* : + x2 : array, optional An N-length array (or scalar) of the x data - *where* : - If *None*, default to fill between everywhere. If not *None*, - it is a N length numpy boolean array and the fill will - only happen over the regions where ``where==True`` + where : array, optional + If *None*, default to fill between everywhere. If not *None*, + it is a N length numpy boolean array and the fill will + only happen over the regions where ``where==True`` + + step_where : {'pre', 'post', 'mid'}, optional + If not None, fill with step logic. + + Notes + ----- *kwargs* : keyword args passed on to the @@ -4610,9 +4623,13 @@ def fill_betweenx(self, y, x1, x2=0, where=None, **kwargs): %(PolyCollection)s + Examples + -------- + .. plot:: mpl_examples/pylab_examples/fill_betweenx_demo.py - .. seealso:: + See Also + -------- :meth:`fill_between` for filling between two sets of y-values @@ -4649,6 +4666,9 @@ def fill_betweenx(self, y, x1, x2=0, where=None, **kwargs): yslice = y[ind0:ind1] x1slice = x1[ind0:ind1] x2slice = x2[ind0:ind1] + if step_where is not None: + step_func = STEP_LOOKUP_MAP[step_where] + yslice, x1slice, x2slice = step_func(yslice, x1slice, x2slice) if not len(yslice): continue From 994b5b601053239cbfd5fb026a09377bc8a944f0 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Fri, 15 May 2015 16:45:52 -0400 Subject: [PATCH 6/9] DOC : make sphinx happy --- lib/matplotlib/axes/_axes.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 4ebd08d92ace..47962db5d401 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -4615,8 +4615,7 @@ def fill_betweenx(self, y, x1, x2=0, where=None, Notes ----- - *kwargs* : - keyword args passed on to the + keyword args passed on to the :class:`~matplotlib.collections.PolyCollection` kwargs control the :class:`~matplotlib.patches.Polygon` properties: From 8c9398f70c66437e958186d49454682f31fa0ae5 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Wed, 20 May 2015 22:51:00 -0400 Subject: [PATCH 7/9] MNT : rename step_where -> step in fill_beteween --- lib/matplotlib/axes/_axes.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 47962db5d401..89cf5986946f 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -4428,7 +4428,7 @@ def fill(self, *args, **kwargs): @docstring.dedent_interpd def fill_between(self, x, y1, y2=0, where=None, interpolate=False, - step_where=None, + step=None, **kwargs): """ Make filled polygons between two curves. @@ -4460,7 +4460,7 @@ def fill_between(self, x, y1, y2=0, where=None, interpolate=False, end points of the filled region will only occur on explicit values in the *x* array. - step_where : {'pre', 'post', 'mid'}, optional + step : {'pre', 'post', 'mid'}, optional If not None, fill with step logic. @@ -4517,8 +4517,8 @@ def fill_between(self, x, y1, y2=0, where=None, interpolate=False, xslice = x[ind0:ind1] y1slice = y1[ind0:ind1] y2slice = y2[ind0:ind1] - if step_where is not None: - step_func = STEP_LOOKUP_MAP[step_where] + if step is not None: + step_func = STEP_LOOKUP_MAP[step] xslice, y1slice, y2slice = step_func(xslice, y1slice, y2slice) if not len(xslice): @@ -4581,7 +4581,7 @@ def get_interp_point(ind): @docstring.dedent_interpd def fill_betweenx(self, y, x1, x2=0, where=None, - step_where=None, **kwargs): + step=None, **kwargs): """ Make filled polygons between two horizontal curves. @@ -4609,7 +4609,7 @@ def fill_betweenx(self, y, x1, x2=0, where=None, it is a N length numpy boolean array and the fill will only happen over the regions where ``where==True`` - step_where : {'pre', 'post', 'mid'}, optional + step : {'pre', 'post', 'mid'}, optional If not None, fill with step logic. Notes @@ -4665,8 +4665,8 @@ def fill_betweenx(self, y, x1, x2=0, where=None, yslice = y[ind0:ind1] x1slice = x1[ind0:ind1] x2slice = x2[ind0:ind1] - if step_where is not None: - step_func = STEP_LOOKUP_MAP[step_where] + if step is not None: + step_func = STEP_LOOKUP_MAP[step] yslice, x1slice, x2slice = step_func(yslice, x1slice, x2slice) if not len(yslice): From e89238b7a78c7d186e8139ce213d3302b367b9a9 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Tue, 14 Jul 2015 04:00:13 -0400 Subject: [PATCH 8/9] DOC: whats_new + example for stepped fill_between --- .travis.yml | 2 +- doc/users/whats_new/2015-05_filledstep.rst | 12 ++ examples/api/filled_step.py | 206 +++++++++++++++++++++ 3 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 doc/users/whats_new/2015-05_filledstep.rst create mode 100644 examples/api/filled_step.py diff --git a/.travis.yml b/.travis.yml index d41c61bdfaf5..0a008a48255f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -67,7 +67,7 @@ install: pip install $PRE python-dateutil $NUMPY pyparsing pillow sphinx!=1.3.0; fi # Always install from pypi - - pip install $PRE nose pep8 + - pip install $PRE nose pep8 cycler # Install mock on python 2. Python 2.6 requires mock 1.0.1 # Since later versions have dropped support diff --git a/doc/users/whats_new/2015-05_filledstep.rst b/doc/users/whats_new/2015-05_filledstep.rst new file mode 100644 index 000000000000..fada39e8737d --- /dev/null +++ b/doc/users/whats_new/2015-05_filledstep.rst @@ -0,0 +1,12 @@ +Add step kwmargs to fill_between +-------------------------------- + +Added ``step`` kwarg to `Axes.fill_between` to allow to fill between +lines drawn using the 'step' draw style. The values of ``step`` match +those of the ``where`` kwarg of `Axes.step`. The asymmetry of of the +kwargs names is not ideal, but `Axes.fill_between` already has a +``where`` kwarg. + +This is particularly useful for plotting pre-binned histograms. + +.. plot:: mpl_examples/api/filled_step.py diff --git a/examples/api/filled_step.py b/examples/api/filled_step.py new file mode 100644 index 000000000000..7b251dde5267 --- /dev/null +++ b/examples/api/filled_step.py @@ -0,0 +1,206 @@ +import itertools +from functools import partial + +import numpy as np +import matplotlib.pyplot as plt +from cycler import cycler +from six.moves import zip + + +def filled_hist(ax, edges, values, bottoms=None, orientation='v', + **kwargs): + """ + Draw a histogram as a stepped patch. + + Extra kwargs are passed through to `fill_between` + + Parameters + ---------- + ax : Axes + The axes to plot to + + edges : array + A an n+1 array giving the left edges of each bin and the + right edge of the last bin. + + values : array + A length n array of bin + + bottoms : scalar or array, optional + A length n array of the bottom of the bars. If None, zero is used. + + orientation : {'v', 'h'} + Orientation of the histogram. 'v' is default value an has + the bars increasing in the positive y-direction. + + Returns + ------- + ret : PolyCollection + Added artist + """ + print(orientation) + if orientation not in set('hv'): + raise ValueError("orientation must be in {'h', 'v'} " + "not {o}".format(o=orientation)) + + kwargs.setdefault('step', 'post') + edges = np.asarray(edges) + values = np.asarray(values) + if len(edges) - 1 != len(values): + raise ValueError('Must provide one more bin edge than value not: ' + 'len(edges): {lb} len(values): {lv}'.format( + lb=len(edges), lv=len(values))) + + if bottoms is None: + bottoms = np.zeros_like(values) + if np.isscalar(bottoms): + bottoms = np.ones_like(values) * bottoms + + values = np.r_[values, values[-1]] + bottoms = np.r_[bottoms, bottoms[-1]] + if orientation == 'h': + return ax.fill_betweenx(edges, values, bottoms, **kwargs) + elif orientation == 'v': + return ax.fill_between(edges, values, bottoms, **kwargs) + else: + raise AssertionError("you should never be here") + + +def stack_hist(ax, stacked_data, sty_cycle, bottoms=None, + hist_func=None, labels=None, + plot_func=None, plot_kwargs=None): + """ + ax : axes.Axes + The axes to add artists too + + stacked_data : array or Mapping + A (N, M) shaped array. The first dimension will be iterated over to + compute histograms row-wise + + sty_cycle : Cycler or operable of dict + Style to apply to each set + + bottoms : array, optional + The initial positions of the bottoms, defaults to 0 + + hist_func : callable, optional + Must have signature `bin_vals, bin_edges = f(data)`. + `bin_edges` expected to be one longer than `bin_vals` + + labels : list of str, optional + The label for each set. + + If not given and stacked data is an array defaults to 'default set {n}' + + If stacked_data is a mapping, and labels is None, default to the keys + (which may come out in a random order). + + If stacked_data is a mapping and labels is given then only + the columns listed by be plotted. + + plot_func : callable, optional + Function to call to draw the histogram must have signature: + + ret = plot_func(ax, edges, top, bottoms=bottoms, + label=label, **kwargs) + + plot_kwargs : dict, optional + Any extra kwargs to pass through to the plotting function. This + will be the same for all calls to the plotting function and will + over-ride the values in cycle. + + Returns + ------- + arts : dict + Dictionary of artists keyed on their labels + """ + # deal with default binning function + if hist_func is None: + hist_func = np.histogram + + # deal with default plotting function + if plot_func is None: + plot_func = filled_hist + + # deal with default + if plot_kwargs is None: + plot_kwargs = {} + print(plot_kwargs) + try: + l_keys = stacked_data.keys() + label_data = True + if labels is None: + labels = l_keys + + except AttributeError: + label_data = False + if labels is None: + labels = itertools.repeat(None) + + if label_data: + loop_iter = enumerate((stacked_data[lab], lab, s) for lab, s in + zip(labels, sty_cycle)) + else: + loop_iter = enumerate(zip(stacked_data, labels, sty_cycle)) + + arts = {} + for j, (data, label, sty) in loop_iter: + if label is None: + label = 'default set {n}'.format(n=j) + label = sty.pop('label', label) + vals, edges = hist_func(data) + if bottoms is None: + bottoms = np.zeros_like(vals) + top = bottoms + vals + print(sty) + sty.update(plot_kwargs) + print(sty) + ret = plot_func(ax, edges, top, bottoms=bottoms, + label=label, **sty) + bottoms = top + arts[label] = ret + ax.legend() + return arts + + +# set up histogram function to fixed bins +edges = np.linspace(-3, 3, 20, endpoint=True) +hist_func = partial(np.histogram, bins=edges) + +# set up style cycles +color_cycle = cycler('facecolor', 'rgbm') +label_cycle = cycler('label', ['set {n}'.format(n=n) for n in range(4)]) +hatch_cycle = cycler('hatch', ['/', '*', '+', '|']) + +# make some synthetic data +stack_data = np.random.randn(4, 12250) +dict_data = {lab: d for lab, d in zip(list(c['label'] for c in label_cycle), + stack_data)} + +# work with plain arrays +fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6), tight_layout=True) +arts = stack_hist(ax1, stack_data, color_cycle + label_cycle + hatch_cycle, + hist_func=hist_func) + +arts = stack_hist(ax2, stack_data, color_cycle, + hist_func=hist_func, + plot_kwargs=dict(edgecolor='w', orientation='h')) +ax1.set_ylabel('counts') +ax1.set_xlabel('x') +ax2.set_xlabel('counts') +ax2.set_ylabel('x') + +# work with labeled data + +fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6), + tight_layout=True, sharey=True) + +arts = stack_hist(ax1, dict_data, color_cycle + hatch_cycle, + hist_func=hist_func) + +arts = stack_hist(ax2, dict_data, color_cycle + hatch_cycle, + hist_func=hist_func, labels=['set 0', 'set 3']) + +ax1.set_ylabel('counts') +ax1.set_xlabel('x') +ax2.set_xlabel('x') From 42eb539552f7ac6ca821d6f1f8ea20f9172bf184 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 16 Jul 2015 21:42:25 -0400 Subject: [PATCH 9/9] DOC: fix typos --- doc/users/whats_new/2015-05_filledstep.rst | 4 ++-- examples/api/filled_step.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/users/whats_new/2015-05_filledstep.rst b/doc/users/whats_new/2015-05_filledstep.rst index fada39e8737d..d9d0a3073b3a 100644 --- a/doc/users/whats_new/2015-05_filledstep.rst +++ b/doc/users/whats_new/2015-05_filledstep.rst @@ -1,5 +1,5 @@ -Add step kwmargs to fill_between --------------------------------- +Add step kwargs to fill_between +------------------------------- Added ``step`` kwarg to `Axes.fill_between` to allow to fill between lines drawn using the 'step' draw style. The values of ``step`` match diff --git a/examples/api/filled_step.py b/examples/api/filled_step.py index 7b251dde5267..1c31dea72b88 100644 --- a/examples/api/filled_step.py +++ b/examples/api/filled_step.py @@ -20,23 +20,23 @@ def filled_hist(ax, edges, values, bottoms=None, orientation='v', The axes to plot to edges : array - A an n+1 array giving the left edges of each bin and the + A length n+1 array giving the left edges of each bin and the right edge of the last bin. values : array - A length n array of bin + A length n array of bin counts or values bottoms : scalar or array, optional A length n array of the bottom of the bars. If None, zero is used. orientation : {'v', 'h'} - Orientation of the histogram. 'v' is default value an has + Orientation of the histogram. 'v' (default) has the bars increasing in the positive y-direction. Returns ------- ret : PolyCollection - Added artist + Artist added to the Axes """ print(orientation) if orientation not in set('hv'): pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy