diff --git a/doc/users/next_whats_new/2018_01_25_scale_axis_by_factor.rst b/doc/users/next_whats_new/2018_01_25_scale_axis_by_factor.rst new file mode 100644 index 000000000000..0d28bbde7b22 --- /dev/null +++ b/doc/users/next_whats_new/2018_01_25_scale_axis_by_factor.rst @@ -0,0 +1,10 @@ +Ability to scale axis by a fixed order of magnitude +--------------------------------------------------- + +To scale an axis by a fixed order of magnitude, set the *scilimits* argument of +``Axes.ticklabel_format`` to the same (non-zero) lower and upper limits. Say to scale +the y axis by a million (1e6), use ``ax.ticklabel_format(style='sci', scilimits=(6, 6), axis='y')``. + +The behavior of ``scilimits=(0, 0)`` is unchanged. With this setting, matplotlib will adjust +the order of magnitude depending on the axis values, rather than keeping it fixed. Previously, setting +``scilimits=(m, m)`` was equivalent to setting ``scilimits=(0, 0)``. diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 259d70064d48..bb2b2b83c71e 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -2672,6 +2672,8 @@ def ticklabel_format(self, **kwargs): be used for numbers outside the range 10`m`:sup: to 10`n`:sup:. Use (0,0) to include all numbers. + Use (m,m) where m <> 0 to fix the order + of magnitude to 10`m`:sup:. *useOffset* [ bool | offset ]; if True, the offset will be calculated as needed; if False, no offset will be used; if a diff --git a/lib/matplotlib/tests/test_ticker.py b/lib/matplotlib/tests/test_ticker.py index 32206a0c6168..c374c1f3f95b 100644 --- a/lib/matplotlib/tests/test_ticker.py +++ b/lib/matplotlib/tests/test_ticker.py @@ -228,6 +228,17 @@ class TestScalarFormatter(object): use_offset_data = [True, False] + scilimits_data = [ + (False, (0, 0), (10.0, 20.0), 0), + (True, (-2, 2), (-10, 20), 0), + (True, (-2, 2), (-20, 10), 0), + (True, (-2, 2), (-110, 120), 2), + (True, (-2, 2), (-120, 110), 2), + (True, (-2, 2), (-.001, 0.002), -3), + (True, (0, 0), (-1e5, 1e5), 5), + (True, (6, 6), (-1e5, 1e5), 6), + ] + @pytest.mark.parametrize('left, right, offset', offset_data) def test_offset_value(self, left, right, offset): fig, ax = plt.subplots() @@ -257,6 +268,18 @@ def test_use_offset(self, use_offset): tmp_form = mticker.ScalarFormatter() assert use_offset == tmp_form.get_useOffset() + @pytest.mark.parametrize( + 'sci_type, scilimits, lim, orderOfMag', scilimits_data) + def test_scilimits(self, sci_type, scilimits, lim, orderOfMag): + tmp_form = mticker.ScalarFormatter() + tmp_form.set_scientific(sci_type) + tmp_form.set_powerlimits(scilimits) + fig, ax = plt.subplots() + ax.yaxis.set_major_formatter(tmp_form) + ax.set_ylim(*lim) + tmp_form.set_locs(ax.yaxis.get_majorticklocs()) + assert orderOfMag == tmp_form.orderOfMagnitude + class FakeAxis(object): """Allow Formatter to be called without having a "full" plot set up.""" diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index c91e78b6ae85..da9d3c6489a8 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -711,10 +711,14 @@ def _compute_offset(self): def _set_orderOfMagnitude(self, range): # if scientific notation is to be used, find the appropriate exponent # if using an numerical offset, find the exponent after applying the - # offset + # offset. When lower power limit = upper <> 0, use provided exponent. if not self._scientific: self.orderOfMagnitude = 0 return + if self._powerlimits[0] == self._powerlimits[1] != 0: + # fixed scaling when lower power limit = upper <> 0. + self.orderOfMagnitude = self._powerlimits[0] + return locs = np.abs(self.locs) if self.offset: oom = math.floor(math.log10(range))