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

Skip to content

ENH : stepfill between #4433

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 9 commits into from
Jul 22, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions doc/users/whats_new/2015-05_filledstep.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
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
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
206 changes: 206 additions & 0 deletions examples/api/filled_step.py
Original file line number Diff line number Diff line change
@@ -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 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 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' (default) has
the bars increasing in the positive y-direction.

Returns
-------
ret : PolyCollection
Artist added to the Axes
"""
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')
80 changes: 54 additions & 26 deletions lib/matplotlib/axes/_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -4428,49 +4428,59 @@ def fill(self, *args, **kwargs):

@docstring.dedent_interpd
def fill_between(self, x, y1, y2=0, where=None, interpolate=False,
step=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
``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`.
step : {'pre', 'post', 'mid'}, optional
If not None, fill with step logic.


Notes
-----

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
Expand Down Expand Up @@ -4507,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 is not None:
step_func = STEP_LOOKUP_MAP[step]
xslice, y1slice, y2slice = step_func(xslice, y1slice, y2slice)

if not len(xslice):
continue
Expand Down Expand Up @@ -4567,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=None, **kwargs):
"""
Make filled polygons between two horizontal curves.

Expand All @@ -4579,31 +4593,42 @@ 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``

*kwargs* :
keyword args passed on to the
step : {'pre', 'post', 'mid'}, optional
If not None, fill with step logic.

Notes
-----

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

.. seealso::
See Also
--------

:meth:`fill_between`
for filling between two sets of y-values
Expand Down Expand Up @@ -4640,6 +4665,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 is not None:
step_func = STEP_LOOKUP_MAP[step]
yslice, x1slice, x2slice = step_func(yslice, x1slice, x2slice)

if not len(yslice):
continue
Expand Down
Loading