diff --git a/doc/users/whats_new.rst b/doc/users/whats_new.rst index 0443dffdfc2c..73f58ce98373 100644 --- a/doc/users/whats_new.rst +++ b/doc/users/whats_new.rst @@ -127,6 +127,17 @@ local intensity of the vector field. .. plot:: mpl_examples/pylab_examples/streamplot_demo.py + +New hist functionality +---------------------- + +Nic Eggert added a new `stacked` kwarg to :meth:`~matplotlib.pyplot.hist` that +allows creation of stacked histograms using any of the histogram types. +Previously, this functionality was only available by using the `barstacked` +histogram type. Now, when `stacked=True` is passed to the function, any of the +histogram types can be stacked. The `barstacked` histogram type retains its +previous functionality for backwards compatibility. + Updated shipped dependencies ---------------------------- diff --git a/examples/pylab_examples/histogram_demo_extended.py b/examples/pylab_examples/histogram_demo_extended.py index 966cc7aaf32d..df8404385b2f 100644 --- a/examples/pylab_examples/histogram_demo_extended.py +++ b/examples/pylab_examples/histogram_demo_extended.py @@ -82,7 +82,19 @@ # P.figure() -n, bins, patches = P.hist(x, 10, normed=1, histtype='barstacked') +n, bins, patches = P.hist(x, 10, normed=1, histtype='bar', stacked=True) + +P.show() + +# +# we can also stack using the step histtype +# + +P.figure() + +n, bins, patches = P.hist(x, 10, histtype='step', stacked=True, fill=True) + +P.show() # # finally: make a multiple-histogram of data-sets with different length diff --git a/lib/matplotlib/axes.py b/lib/matplotlib/axes.py index c6115693d38e..49f0c70914d7 100644 --- a/lib/matplotlib/axes.py +++ b/lib/matplotlib/axes.py @@ -7728,7 +7728,7 @@ def get_shared_y_axes(self): def hist(self, x, bins=10, range=None, normed=False, weights=None, cumulative=False, bottom=None, histtype='bar', align='mid', orientation='vertical', rwidth=None, log=False, - color=None, label=None, + color=None, label=None, stacked=False, **kwargs): """ Plot a histogram. @@ -7738,7 +7738,7 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, hist(x, bins=10, range=None, normed=False, weights=None, cumulative=False, bottom=None, histtype='bar', align='mid', orientation='vertical', rwidth=None, log=False, - color=None, label=None, + color=None, label=None, stacked=False, **kwargs) Compute and draw the histogram of *x*. The return value is a @@ -7862,6 +7862,11 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, ax.hist(12+3*np.random.randn(1000), label='women', alpha=0.5) ax.legend() + *stacked*: + If *True*, multiple data are stacked on top of each other + If *False* multiple data are aranged side by side if + histtype is 'bar' or on top of each other if histtype is 'step' + . kwargs are used to update the properties of the @@ -7901,6 +7906,9 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, 'hist now uses the rwidth to give relative width ' 'and not absolute width') + if histtype == 'barstacked' and not stacked: + stacked=True + # Massage 'x' for processing. # NOTE: Be sure any changes here is also done below to 'weights' if isinstance(x, np.ndarray) or not iterable(x[0]): @@ -7986,13 +7994,21 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, hist_kwargs['new'] = True n = [] - for i in xrange(nx): + mlast = bottom + # reversed order is necessary so when stacking histogram, first dataset is on top + # if histogram isn't stacked, this doesn't make any difference + for i in reversed(xrange(nx)): # this will automatically overwrite bins, # so that each histogram uses the same bins m, bins = np.histogram(x[i], bins, weights=w[i], **hist_kwargs) + if mlast is None: + mlast = np.zeros(len(bins)-1, np.int) if normed: db = np.diff(bins) m = (m.astype(float) / db) / m.sum() + if stacked: + m += mlast + mlast[:] = m n.append(m) if cumulative: @@ -8005,6 +8021,8 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, else: n = [m[slc].cumsum()[slc] for m in n] + n.reverse() # put them back in the right order + patches = [] if histtype.startswith('bar'): @@ -8017,7 +8035,7 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, else: dr = 1.0 - if histtype=='bar': + if histtype=='bar' and not stacked: width = dr*totwidth/nx dw = width @@ -8026,10 +8044,9 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, else: boffset = 0.0 stacked = False - elif histtype=='barstacked': + elif histtype=='barstacked' or stacked: width = dr*totwidth boffset, dw = 0.0, 0.0 - stacked = True if align == 'mid' or align == 'edge': boffset += 0.5*totwidth @@ -8042,14 +8059,10 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, _barfunc = self.bar for m, c in zip(n, color): - patch = _barfunc(bins[:-1]+boffset, m, width, bottom, + patch = _barfunc(bins[:-1]+boffset, m, width, align='center', log=log, color=c) patches.append(patch) - if stacked: - if bottom is None: - bottom = 0.0 - bottom += m boffset += dw elif histtype.startswith('step'): @@ -8072,6 +8085,8 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None, else: # orientation == 'vertical' self.set_yscale('log') + # If fill kwarg is set, it will be passed to the patch collection, + # overriding this fill = (histtype == 'stepfilled') for m, c in zip(n, color): diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index dcd46f87beb6..d4218ad40b41 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -2642,7 +2642,8 @@ def hexbin(x, y, C=None, gridsize=100, bins=None, xscale='linear', @_autogen_docstring(Axes.hist) def hist(x, bins=10, range=None, normed=False, weights=None, cumulative=False, bottom=None, histtype='bar', align='mid', orientation='vertical', - rwidth=None, log=False, color=None, label=None, hold=None, **kwargs): + rwidth=None, log=False, color=None, label=None, stacked=False, + hold=None, **kwargs): ax = gca() # allow callers to override the hold state by passing hold=True|False washold = ax.ishold() diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked.pdf b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked.pdf new file mode 100644 index 000000000000..cc60f693d67f Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked.pdf differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked.png b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked.png new file mode 100644 index 000000000000..559ff8a50923 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked.svg b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked.svg new file mode 100644 index 000000000000..5dc660ae8fd4 --- /dev/null +++ b/lib/matplotlib/tests/baseline_images/test_axes/hist_stacked.svg @@ -0,0 +1,558 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index de7fb8bcb020..337f02724e93 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -856,6 +856,15 @@ def test_errorbar(): fig.suptitle('Variable errorbars') +@image_comparison(baseline_images=['hist_stacked']) +def test_hist_stacked(): + # make some data + d1 = np.linspace(0, 10, 50) + d2 = np.linspace(1, 3, 20) + fig = plt.figure() + ax = fig.add_subplot(111) + ax.hist( (d1, d2), histtype="stepfilled", stacked=True) + if __name__=='__main__': import nose nose.runmodule(argv=['-s','--with-doctest'], exit=False)