diff --git a/lib/matplotlib/axes/_axes.py b/lib/matplotlib/axes/_axes.py index 4630a7568818..b63c810d210e 100644 --- a/lib/matplotlib/axes/_axes.py +++ b/lib/matplotlib/axes/_axes.py @@ -36,6 +36,30 @@ from matplotlib.axes._base import _AxesBase, _process_plot_format from matplotlib.axes._secondary_axes import SecondaryAxis +try: + from numpy.lib.histograms import histogram_bin_edges +except ImportError: + # this function is new in np 1.15 + def histogram_bin_edges(arr, bins, range=None, weights=None): + # this in True for 1D arrays, and False for None and str + if np.ndim(bins) == 1: + return bins + + if isinstance(bins, str): + # rather than backporting the internals, just do the full + # computation. If this is too slow for users, they can + # update numpy, or pick a manual number of bins + return np.histogram(arr, bins, range, weights)[1] + else: + if bins is None: + # hard-code numpy's default + bins = 10 + if range is None: + range = np.min(arr), np.max(arr) + + return np.linspace(*range, bins + 1) + + _log = logging.getLogger(__name__) @@ -6611,9 +6635,6 @@ def hist(self, x, bins=None, range=None, density=None, weights=None, if bin_range is not None: bin_range = self.convert_xunits(bin_range) - # Check whether bins or range are given explicitly. - binsgiven = np.iterable(bins) or bin_range is not None - # We need to do to 'weights' what was done to 'x' if weights is not None: w = cbook._reshape_2D(weights, 'weights') @@ -6638,22 +6659,42 @@ def hist(self, x, bins=None, range=None, density=None, weights=None, "sets and %d colors were provided" % (nx, len(color))) raise ValueError(error_message) - # If bins are not specified either explicitly or via range, - # we need to figure out the range required for all datasets, - # and supply that to np.histogram. - if not binsgiven and not input_empty: + hist_kwargs = dict() + + # if the bin_range is not given, compute without nan numpy + # does not do this for us when guessing the range (but will + # happily ignore nans when computing the histogram). + if bin_range is None: xmin = np.inf xmax = -np.inf for xi in x: - if len(xi) > 0: + if len(xi): + # python's min/max ignore nan, + # np.minnan returns nan for all nan input xmin = min(xmin, np.nanmin(xi)) xmax = max(xmax, np.nanmax(xi)) - bin_range = (xmin, xmax) + # make sure we have seen at least one non-nan and finite + # value before we reset the bin range + if not np.isnan([xmin, xmax]).any() and not (xmin > xmax): + bin_range = (xmin, xmax) + + # If bins are not specified either explicitly or via range, + # we need to figure out the range required for all datasets, + # and supply that to np.histogram. + if not input_empty and len(x) > 1: + if weights is not None: + _w = np.concatenate(w) + else: + _w = None + + bins = histogram_bin_edges(np.concatenate(x), + bins, bin_range, _w) + else: + hist_kwargs['range'] = bin_range + density = bool(density) or bool(normed) if density and not stacked: - hist_kwargs = dict(range=bin_range, density=density) - else: - hist_kwargs = dict(range=bin_range) + hist_kwargs = dict(density=density) # List to store all the top coordinates of the histograms tops = [] diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 741a9c2b574a..3e5d00b16f06 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -6346,3 +6346,23 @@ def test_datetime_masked(): ax.plot(x, m) # these are the default viewlim assert ax.get_xlim() == (730120.0, 733773.0) + + +def test_hist_auto_bins(): + _, bins, _ = plt.hist([[1, 2, 3], [3, 4, 5, 6]], bins='auto') + assert bins[0] <= 1 + assert bins[-1] >= 6 + + +def test_hist_nan_data(): + fig, (ax1, ax2) = plt.subplots(2) + + data = [1, 2, 3] + nan_data = data + [np.nan] + + bins, edges, _ = ax1.hist(data) + with np.errstate(invalid='ignore'): + nanbins, nanedges, _ = ax2.hist(nan_data) + + assert np.allclose(bins, nanbins) + assert np.allclose(edges, nanedges) 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