diff --git a/doc/users/next_whats_new/multiplelocator_offset.rst b/doc/users/next_whats_new/multiplelocator_offset.rst new file mode 100644 index 000000000000..863fdb3c4d7e --- /dev/null +++ b/doc/users/next_whats_new/multiplelocator_offset.rst @@ -0,0 +1,17 @@ +``offset`` parameter for MultipleLocator +---------------------------------------- + +An *offset* may now be specified to shift all the ticks by the given value. + +.. plot:: + :include-source: true + + import matplotlib.pyplot as plt + import matplotlib.ticker as mticker + + _, ax = plt.subplots() + ax.plot(range(10)) + locator = mticker.MultipleLocator(base=3, offset=0.3) + ax.xaxis.set_major_locator(locator) + + plt.show() diff --git a/galleries/examples/ticks/tick-locators.py b/galleries/examples/ticks/tick-locators.py index e634e022173c..6cf4afaf22d7 100644 --- a/galleries/examples/ticks/tick-locators.py +++ b/galleries/examples/ticks/tick-locators.py @@ -37,8 +37,8 @@ def setup(ax, title): axs[0].xaxis.set_minor_locator(ticker.NullLocator()) # Multiple Locator -setup(axs[1], title="MultipleLocator(0.5)") -axs[1].xaxis.set_major_locator(ticker.MultipleLocator(0.5)) +setup(axs[1], title="MultipleLocator(0.5, offset=0.2)") +axs[1].xaxis.set_major_locator(ticker.MultipleLocator(0.5, offset=0.2)) axs[1].xaxis.set_minor_locator(ticker.MultipleLocator(0.1)) # Fixed Locator @@ -53,7 +53,7 @@ def setup(ax, title): # Index Locator setup(axs[4], title="IndexLocator(base=0.5, offset=0.25)") -axs[4].plot(range(0, 5), [0]*5, color='white') +axs[4].plot([0]*5, color='white') axs[4].xaxis.set_major_locator(ticker.IndexLocator(base=0.5, offset=0.25)) # Auto Locator diff --git a/lib/matplotlib/tests/test_ticker.py b/lib/matplotlib/tests/test_ticker.py index 3d38df575f09..53224d373f80 100644 --- a/lib/matplotlib/tests/test_ticker.py +++ b/lib/matplotlib/tests/test_ticker.py @@ -63,6 +63,12 @@ def test_basic(self): 9.441, 12.588]) assert_almost_equal(loc.tick_values(-7, 10), test_value) + def test_basic_with_offset(self): + loc = mticker.MultipleLocator(base=3.147, offset=1.2) + test_value = np.array([-8.241, -5.094, -1.947, 1.2, 4.347, 7.494, + 10.641]) + assert_almost_equal(loc.tick_values(-7, 10), test_value) + def test_view_limits(self): """ Test basic behavior of view limits. @@ -80,6 +86,15 @@ def test_view_limits_round_numbers(self): loc = mticker.MultipleLocator(base=3.147) assert_almost_equal(loc.view_limits(-4, 4), (-6.294, 6.294)) + def test_view_limits_round_numbers_with_offset(self): + """ + Test that everything works properly with 'round_numbers' for auto + limit. + """ + with mpl.rc_context({'axes.autolimit_mode': 'round_numbers'}): + loc = mticker.MultipleLocator(base=3.147, offset=1.3) + assert_almost_equal(loc.view_limits(-4, 4), (-4.994, 4.447)) + def test_set_params(self): """ Create multiple locator with 0.7 base, and change it to something else. @@ -88,6 +103,8 @@ def test_set_params(self): mult = mticker.MultipleLocator(base=0.7) mult.set_params(base=1.7) assert mult._edge.step == 1.7 + mult.set_params(offset=3) + assert mult._offset == 3 class TestAutoMinorLocator: diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index bf059567f5fe..0877e58c5fe7 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -1831,17 +1831,41 @@ def view_limits(self, vmin, vmax): class MultipleLocator(Locator): """ - Set a tick on each integer multiple of the *base* within the view - interval. + Set a tick on each integer multiple of the *base* plus an *offset* within + the view interval. """ - def __init__(self, base=1.0): + def __init__(self, base=1.0, offset=0.0): + """ + Parameters + ---------- + base : float > 0 + Interval between ticks. + offset : float + Value added to each multiple of *base*. + + .. versionadded:: 3.8 + """ self._edge = _Edge_integer(base, 0) + self._offset = offset - def set_params(self, base): - """Set parameters within this locator.""" + def set_params(self, base=None, offset=None): + """ + Set parameters within this locator. + + Parameters + ---------- + base : float > 0 + Interval between ticks. + offset : float + Value added to each multiple of *base*. + + .. versionadded:: 3.8 + """ if base is not None: self._edge = _Edge_integer(base, 0) + if offset is not None: + self._offset = offset def __call__(self): """Return the locations of the ticks.""" @@ -1852,19 +1876,20 @@ def tick_values(self, vmin, vmax): if vmax < vmin: vmin, vmax = vmax, vmin step = self._edge.step + vmin -= self._offset + vmax -= self._offset vmin = self._edge.ge(vmin) * step n = (vmax - vmin + 0.001 * step) // step - locs = vmin - step + np.arange(n + 3) * step + locs = vmin - step + np.arange(n + 3) * step + self._offset return self.raise_if_exceeds(locs) def view_limits(self, dmin, dmax): """ - Set the view limits to the nearest multiples of *base* that - contain the data. + Set the view limits to the nearest tick values that contain the data. """ if mpl.rcParams['axes.autolimit_mode'] == 'round_numbers': - vmin = self._edge.le(dmin) * self._edge.step - vmax = self._edge.ge(dmax) * self._edge.step + vmin = self._edge.le(dmin - self._offset) * self._edge.step + self._offset + vmax = self._edge.ge(dmax - self._offset) * self._edge.step + self._offset if vmin == vmax: vmin -= 1 vmax += 1 diff --git a/lib/matplotlib/ticker.pyi b/lib/matplotlib/ticker.pyi index e53a665432e2..1f4239ef2718 100644 --- a/lib/matplotlib/ticker.pyi +++ b/lib/matplotlib/ticker.pyi @@ -212,9 +212,8 @@ class LinearLocator(Locator): ) -> None: ... class MultipleLocator(Locator): - def __init__(self, base: float = ...) -> None: ... - # Makes set_params `base` argument mandatory - def set_params(self, base: float | None) -> None: ... # type: ignore[override] + def __init__(self, base: float = ..., offset: float = ...) -> None: ... + def set_params(self, base: float | None = ..., offset: float | None = ...) -> None: ... def view_limits(self, dmin: float, dmax: float) -> tuple[float, float]: ... class _Edge_integer: 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