diff --git a/lib/matplotlib/tests/baseline_images/test_axes/auto_numticks_log.png b/lib/matplotlib/tests/baseline_images/test_axes/auto_numticks_log.png new file mode 100644 index 000000000000..248fb669c56a Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/auto_numticks_log.png differ diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 737cff66647c..d4ba30e59d12 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -4418,9 +4418,19 @@ def test_adjust_numtick_aspect(): @image_comparison(baseline_images=["auto_numticks"], style='default', extensions=['png']) def test_auto_numticks(): + # Make tiny, empty subplots, verify that there are only 3 ticks. fig, axes = plt.subplots(4, 4) +@image_comparison(baseline_images=["auto_numticks_log"], style='default', + extensions=['png']) +def test_auto_numticks_log(): + # Verify that there are not too many ticks with a large log range. + fig, ax = plt.subplots() + matplotlib.rcParams['axes.autolimit_mode'] = 'round_numbers' + ax.loglog([1e-20, 1e5], [1e-16, 10]) + + @cleanup def test_broken_barh_empty(): fig, ax = plt.subplots() diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index 02109e8bd991..ff0a71d156d2 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -1639,8 +1639,11 @@ def set_params(self, **kwargs): def _raw_ticks(self, vmin, vmax): if self._nbins == 'auto': - nbins = max(min(self.axis.get_tick_space(), 9), - max(1, self._min_n_ticks - 1)) + if self.axis is not None: + nbins = max(min(self.axis.get_tick_space(), 9), + max(1, self._min_n_ticks - 1)) + else: + nbins = 9 else: nbins = self._nbins @@ -1757,15 +1760,19 @@ class LogLocator(Locator): Determine the tick locations for log axes """ - def __init__(self, base=10.0, subs=(1.0,), numdecs=4, numticks=15): + def __init__(self, base=10.0, subs=(1.0,), numdecs=4, numticks=None): """ place ticks on the location= base**i*subs[j] """ + if numticks is None: + if rcParams['_internal.classic_mode']: + numticks = 15 + else: + numticks = 'auto' self.base(base) self.subs(subs) - # this needs to be validated > 1 with traitlets - self.numticks = numticks self.numdecs = numdecs + self.numticks = numticks def set_params(self, base=None, subs=None, numdecs=None, numticks=None): """Set parameters within this locator.""" @@ -1786,7 +1793,7 @@ def base(self, base): def subs(self, subs): """ - set the minor ticks the log scaling every base**i*subs[j] + set the minor ticks for the log scaling every base**i*subs[j] """ if subs is None: self._subs = None # autosub @@ -1799,6 +1806,14 @@ def __call__(self): return self.tick_values(vmin, vmax) def tick_values(self, vmin, vmax): + if self.numticks == 'auto': + if self.axis is not None: + numticks = max(min(self.axis.get_tick_space(), 9), 2) + else: + numticks = 9 + else: + numticks = self.numticks + b = self._base # dummy axis has no axes attribute if hasattr(self.axis, 'axes') and self.axis.axes.name == 'polar': @@ -1836,12 +1851,14 @@ def tick_values(self, vmin, vmax): subs = self._subs stride = 1 - if not self.numticks > 1: - raise RuntimeError('The number of ticks must be greater than 1 ' - 'for LogLocator.') - # FIXME: The following was designed for integer division in py2. - while numdec / stride + 1 > self.numticks: - stride += 1 + + if rcParams['_internal.classic_mode']: + # Leave the bug left over from the PY2-PY3 transition. + while numdec / stride + 1 > numticks: + stride += 1 + else: + while numdec // stride + 1 > numticks: + stride += 1 decades = np.arange(math.floor(vmin) - stride, math.ceil(vmax) + 2 * stride, stride)