8000 Merge pull request #25542 from rcomer/multiple-locator-offset · matplotlib/matplotlib@f7524f4 · GitHub
[go: up one dir, main page]

Skip to content

Commit f7524f4

Browse files
authored
Merge pull request #25542 from rcomer/multiple-locator-offset
ENH: offset parameter for MultipleLocator
2 parents 4a0ffb8 + 1ad052e commit f7524f4

File tree

5 files changed

+74
-16
lines changed

5 files changed

+74
-16
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
``offset`` parameter for MultipleLocator
2+
----------------------------------------
3+
4+
An *offset* may now be specified to shift all the ticks by the given value.
5+
6+
.. plot::
7+
:include-source: true
8+
9+
import matplotlib.pyplot as plt
10+
import matplotlib.ticker as mticker
11+
12+
_, ax = plt.subplots()
13+
ax.plot(range(10))
14+
locator = mticker.MultipleLocator(base=3, offset=0.3)
15+
ax.xaxis.set_major_locator(locator)
16+
17+
plt.show()

galleries/examples/ticks/tick-locators.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ def setup(ax, title):
3737
axs[0].xaxis.set_minor_locator(ticker.NullLocator())
3838

3939
# Multiple Locator
40-
setup(axs[1], title="MultipleLocator(0.5)")
41-
axs[1].xaxis.set_major_locator(ticker.MultipleLocator(0.5))
40+
setup(axs[1], title="MultipleLocator(0.5, offset=0.2)")
41+
axs[1].xaxis.set_major_locator(ticker.MultipleLocator(0.5, offset=0.2))
4242
axs[1].xaxis.set_minor_locator(ticker.MultipleLocator(0.1))
4343

4444
# Fixed Locator
@@ -53,7 +53,7 @@ def setup(ax, title):
5353

5454
# Index Locator
5555
setup(axs[4], title="IndexLocator(base=0.5, offset=0.25)")
56-
axs[4].plot(range(0, 5), [0]*5, color='white')
56+
axs[4].plot([0]*5, color='white')
5757
axs[4].xaxis.set_major_locator(ticker.IndexLocator(base=0.5, offset=0.25))
5858

5959
# Auto Locator

lib/matplotlib/tests/test_ticker.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@ def test_basic(self):
6363
9.441, 12.588])
6464
assert_almost_equal(loc.tick_values(-7, 10), test_value)
6565

66+
def test_basic_with_offset(self):
67+
loc = mticker.MultipleLocator(base=3.147, offset=1.2)
68+
test_value = np.array([-8.241, -5.094, -1.947, 1.2, 4.347, 7.494,
69+
10.641])
70+
assert_almost_equal(loc.tick_values(-7, 10), test_value)
71+
6672
def test_view_limits(self):
6773
"""
6874
Test basic behavior of view limits.
@@ -80,6 +86,15 @@ def test_view_limits_round_numbers(self):
8086
loc = mticker.MultipleLocator(base=3.147)
8187
assert_almost_equal(loc.view_limits(-4, 4), (-6.294, 6.294))
8288

89+
def test_view_limits_round_numbers_with_offset(self):
90+
"""
91+
Test that everything works properly with 'round_numbers' for auto
92+
limit.
93+
"""
94+
with mpl.rc_context({'axes.autolimit_mode': 'round_numbers'}):
95+
loc = mticker.MultipleLocator(base=3.147, offset=1.3)
96+
assert_almost_equal(loc.view_limits(-4, 4), (-4.994, 4.447))
97+
8398
def test_set_params(self):
8499
"""
85100
Create multiple locator with 0.7 base, and change it to something else.
@@ -88,6 +103,8 @@ def test_set_params(self):
88103
mult = mticker.MultipleLocator(base=0.7)
89104
mult.set_params(base=1.7)
90105
assert mult._edge.step == 1.7
106+
mult.set_params(offset=3)
107+
assert mult._offset == 3
91108

92109

93110
class TestAutoMinorLocator:

lib/matplotlib/ticker.py

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1831,17 +1831,41 @@ def view_limits(self, vmin, vmax):
18311831

18321832
class MultipleLocator(Locator):
18331833
"""
1834-
Set a tick on each integer multiple of the *base* within the view
1835-
interval.
1834+
Set a tick on each integer multiple of the *base* plus an *offset* within
1835+
the view interval.
18361836
"""
18371837

1838-
def __init__(self, base=1.0):
1838+
def __init__(self, base=1.0, offset=0.0):
1839+
"""
1840+
Parameters
1841+
----------
1842+
base : float > 0
1843+
Interval between ticks.
1844+
offset : float
1845+
Value added to each multiple of *base*.
1846+
1847+
.. versionadded:: 3.8
1848+
"""
18391849
self._edge = _Edge_integer(base, 0)
1850+
self._offset = offset
18401851

1841-
def set_params(self, base):
1842-
"""Set parameters within this locator."""
1852+
def set_params(self, base=None, offset=None):
1853+
"""
1854+
Set parameters within this locator.
1855+
1856+
Parameters
1857+
----------
1858+
base : float > 0
1859+
Interval between ticks.
1860+
offset : float
1861+
Value added to each multiple of *base*.
1862+
1863+
.. versionadded:: 3.8
1864+
"""
18431865
if base is not None:
18441866
self._edge = _Edge_integer(base, 0)
1867+
if offset is not None:
1868+
self._offset = offset
18451869

18461870
def __call__(self):
18471871
"""Return the locations of the ticks."""
@@ -1852,19 +1876,20 @@ def tick_values(self, vmin, vmax):
18521876
if vmax < vmin:
18531877
vmin, vmax = vmax, vmin
18541878
step = self._edge.step
1879+
vmin -= self._offset
1880+
vmax -= self._offset
18551881
vmin = self._edge.ge(vmin) * step
18561882
n = (vmax - vmin + 0.001 * step) // step
1857-
locs = vmin - step + np.arange(n + 3) * step
1883+
locs = vmin - step + np.arange(n + 3) * step + self._offset
18581884
return self.raise_if_exceeds(locs)
18591885

18601886
def view_limits(self, dmin, dmax):
18611887
"""
1862-
Set the view limits to the nearest multiples of *base* that
1863-
contain the data.
1888+
Set the view limits to the nearest tick values that contain the data.
18641889
"""
18651890
if mpl.rcParams['axes.autolimit_mode'] == 'round_numbers':
1866-
vmin = self._edge.le(dmin) * self._edge.step
1867-
vmax = self._edge.ge(dmax) * self._edge.step
1891+
vmin = self._edge.le(dmin - self._offset) * self._edge.step + self._offset
1892+
vmax = self._edge.ge(dmax - self._offset) * self._edge.step + self._offset
18681893
if vmin == vmax:
18691894
vmin -= 1
18701895
vmax += 1

lib/matplotlib/ticker.pyi

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,8 @@ class LinearLocator(Locator):
212212
) -> None: ...
213213

214214
class MultipleLocator(Locator):
215-
def __init__(self, base: float = ...) -> None: ...
216-
# Makes set_params `base` argument mandatory
217-
def set_params(self, base: float | None) -> None: ... # type: ignore[override]
215+
def __init__(self, base: float = ..., offset: float = ...) -> None: ...
216+
def set_params(self, base: float | None = ..., offset: float | None = ...) -> None: ...
218217
def view_limits(self, dmin: float, dmax: float) -> tuple[float, float]: ...
219218

220219
class _Edge_integer:

0 commit comments

Comments
 (0)
0