Skip to content

Commit f27cde1

Browse files
authored
Merge pull request #8638 from tacaswell/fix_auto_hist_range
FIX: if bins input to hist is str, treat like no bins
2 parents c94d0ed + c6f0554 commit f27cde1

File tree

2 files changed

+73
-12
lines changed

2 files changed

+73
-12
lines changed

lib/matplotlib/axes/_axes.py

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,30 @@
3636
from matplotlib.axes._base import _AxesBase, _process_plot_format
3737
from matplotlib.axes._secondary_axes import SecondaryAxis
3838

39+
try:
40+
from numpy.lib.histograms import histogram_bin_edges
41+
except ImportError:
42+
# this function is new in np 1.15
43+
def histogram_bin_edges(arr, bins, range=None, weights=None):
44+
# this in True for 1D arrays, and False for None and str
45+
if np.ndim(bins) == 1:
46+
return bins
47+
48+
if isinstance(bins, str):
49+
# rather than backporting the internals, just do the full
50+
# computation. If this is too slow for users, they can
51+
# update numpy, or pick a manual number of bins
52+
return np.histogram(arr, bins, range, weights)[1]
53+
else:
54+
if bins is None:
55+
# hard-code numpy's default
56+
bins = 10
57+
if range is None:
58+
range = np.min(arr), np.max(arr)
59+
60+
return np.linspace(*range, bins + 1)
61+
62+
3963
_log = logging.getLogger(__name__)
4064

4165

@@ -6611,9 +6635,6 @@ def hist(self, x, bins=None, range=None, density=None, weights=None,
66116635
if bin_range is not None:
66126636
bin_range = self.convert_xunits(bin_range)
66136637

6614-
# Check whether bins or range are given explicitly.
6615-
binsgiven = np.iterable(bins) or bin_range is not None
6616-
66176638
# We need to do to 'weights' what was done to 'x'
66186639
if weights is not None:
66196640
w = cbook._reshape_2D(weights, 'weights')
@@ -6638,22 +6659,42 @@ def hist(self, x, bins=None, range=None, density=None, weights=None,
66386659
"sets and %d colors were provided" % (nx, len(color)))
66396660
raise ValueError(error_message)
66406661

6641-
# If bins are not specified either explicitly or via range,
6642-
# we need to figure out the range required for all datasets,
6643-
# and supply that to np.histogram.
6644-
if not binsgiven and not input_empty:
6662+
hist_kwargs = dict()
6663+
6664+
# if the bin_range is not given, compute without nan numpy
6665+
# does not do this for us when guessing the range (but will
6666+
# happily ignore nans when computing the histogram).
6667+
if bin_range is None:
66456668
xmin = np.inf
66466669
xmax = -np.inf
66476670
for xi in x:
6648-
if len(xi) > 0:
6671+
if len(xi):
6672+
# python's min/max ignore nan,
6673+
# np.minnan returns nan for all nan input
66496674
xmin = min(xmin, np.nanmin(xi))
66506675
xmax = max(xmax, np.nanmax(xi))
6651-
bin_range = (xmin, xmax)
6676+
# make sure we have seen at least one non-nan and finite
6677+
# value before we reset the bin range
6678+
if not np.isnan([xmin, xmax]).any() and not (xmin > xmax):
6679+
bin_range = (xmin, xmax)
6680+
6681+
# If bins are not specified either explicitly or via range,
6682+
# we need to figure out the range required for all datasets,
6683+
# and supply that to np.histogram.
6684+
if not input_empty and len(x) > 1:
6685+
if weights is not None:
6686+
_w = np.concatenate(w)
6687+
else:
6688+
_w = None
6689+
6690+
bins = histogram_bin_edges(np.concatenate(x),
6691+
bins, bin_range, _w)
6692+
else:
6693+
hist_kwargs['range'] = bin_range
6694+
66526695
density = bool(density) or bool(normed)
66536696
if density and not stacked:
6654-
hist_kwargs = dict(range=bin_range, density=density)
6655-
else:
6656-
hist_kwargs = dict(range=bin_range)
6697+
hist_kwargs = dict(density=density)
66576698

66586699
# List to store all the top coordinates of the histograms
66596700
tops = []

lib/matplotlib/tests/test_axes.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6346,3 +6346,23 @@ def test_datetime_masked():
63466346
ax.plot(x, m)
63476347
# these are the default viewlim
63486348
assert ax.get_xlim() == (730120.0, 733773.0)
6349+
6350+
6351+
def test_hist_auto_bins():
6352+
_, bins, _ = plt.hist([[1, 2, 3], [3, 4, 5, 6]], bins='auto')
6353+
assert bins[0] <= 1
6354+
assert bins[-1] >= 6
6355+
6356+
6357+
def test_hist_nan_data():
6358+
fig, (ax1, ax2) = plt.subplots(2)
6359+
6360+
data = [1, 2, 3]
6361+
nan_data = data + [np.nan]
6362+
6363+
bins, edges, _ = ax1.hist(data)
6364+
with np.errstate(invalid='ignore'):
6365+
nanbins, nanedges, _ = ax2.hist(nan_data)
6366+
6367+
assert np.allclose(bins, nanbins)
6368+
assert np.allclose(edges, nanedges)

0 commit comments

Comments
 (0)