Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7488da6

Browse files
efiringtacaswell
authored andcommitted
Merge pull request #5588 from mdboom/dynamic-ticking
Adjust number of ticks based on length of axis
1 parent 8ac5b4b commit 7488da6

File tree

5 files changed

+55
-3
lines changed

5 files changed

+55
-3
lines changed

lib/matplotlib/axis.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,7 @@ def __init__(self, axes, pickradius=15):
664664
# Initialize here for testing; later add API
665665
self._major_tick_kw = dict()
666666
self._minor_tick_kw = dict()
667+
self._tick_space = None
667668

668669
self.cla()
669670
self._set_scale('linear')
@@ -795,6 +796,7 @@ def set_tick_params(self, which='major', reset=False, **kw):
795796
for tick in self.minorTicks:
796797
tick._apply_params(**self._minor_tick_kw)
797798
self.stale = True
799+
self._tick_space = None
798800

799801
@staticmethod
800802
def _translate_tick_kw(kw, to_init_kw=True):
@@ -1663,6 +1665,13 @@ def axis_date(self, tz=None):
16631665
tz = pytz.timezone(tz)
16641666
self.update_units(datetime.datetime(2009, 1, 1, 0, 0, 0, 0, tz))
16651667

1668+
def get_tick_space(self):
1669+
"""
1670+
Return the estimated number of ticks that can fit on the axis.
1671+
"""
1672+
# Must be overridden in the subclass
1673+
raise NotImplementedError()
1674+
16661675

16671676
class XAxis(Axis):
16681677
__name__ = 'xaxis'
@@ -1986,6 +1995,18 @@ def set_default_intervals(self):
19861995
self.axes.viewLim.intervalx = xmin, xmax
19871996
self.stale = True
19881997

1998+
def get_tick_space(self):
1999+
if self._tick_space is None:
2000+
ends = self.axes.transAxes.transform([[0, 0], [1, 0]])
2001+
length = ((ends[1][0] - ends[0][0]) / self.axes.figure.dpi) * 72.0
2002+
tick = self._get_tick(True)
2003+
# There is a heuristic here that the aspect ratio of tick text
2004+
# is no more than 3:1
2005+
size = tick.label1.get_size() * 3
2006+
size *= np.cos(np.deg2rad(tick.label1.get_rotation()))
2007+
self._tick_space = np.floor(length / size)
2008+
return self._tick_space
2009+
19892010

19902011
class YAxis(Axis):
19912012
__name__ = 'yaxis'
@@ -2316,3 +2337,14 @@ def set_default_intervals(self):
23162337
if not viewMutated:
23172338
self.axes.viewLim.intervaly = ymin, ymax
23182339
self.stale = True
2340+
2341+
def get_tick_space(self):
2342+
if self._tick_space is None:
2343+
ends = self.axes.transAxes.transform([[0, 0], [0, 1]])
2344+
length = ((ends[1][1] - ends[0][1]) / self.axes.figure.dpi) * 72.0
2345+
tick = self._get_tick(True)
2346+
# Having a spacing of at least 2 just looks good.
2347+
size = tick.label1.get_size() * 2.0
2348+
size *= np.cos(np.deg2rad(tick.label1.get_rotation()))
2349+
self._tick_space = np.floor(length / size)
2350+
return self._tick_space

lib/matplotlib/tests/test_axes.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4236,6 +4236,12 @@ def _helper_y(ax):
42364236
assert assert_array_equal(ax_lst[0][1].get_xlim(), orig_xlim)
42374237

42384238

4239+
@image_comparison(baseline_images=["auto_numticks"], style='default',
4240+
extensions=['png'])
4241+
def test_auto_numticks():
4242+
fig, axes = plt.subplots(4, 4)
4243+
4244+
42394245
if __name__ == '__main__':
42404246
import nose
42414247
import sys

lib/matplotlib/ticker.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ def get_data_interval(self):
202202
def set_data_interval(self, vmin, vmax):
203203
self.dataLim.intervalx = vmin, vmax
204204

205+
def get_tick_space(self):
206+
# Just use the long-standing default of nbins==9
207+
return 9
208+
205209

206210
class TickHelper(object):
207211
axis = None
@@ -1355,7 +1359,9 @@ def __init__(self, *args, **kwargs):
13551359
Keyword args:
13561360
13571361
*nbins*
1358-
Maximum number of intervals; one less than max number of ticks.
1362+
Maximum number of intervals; one less than max number of
1363+
ticks. If the string `'auto'`, the number of bins will be
1364+
automatically determined based on the length of the axis.
13591365
13601366
*steps*
13611367
Sequence of nice numbers starting with 1 and ending with 10;
@@ -1393,7 +1399,9 @@ def __init__(self, *args, **kwargs):
13931399
def set_params(self, **kwargs):
13941400
"""Set parameters within this locator."""
13951401
if 'nbins' in kwargs:
1396-
self._nbins = int(kwargs['nbins'])
1402+
self._nbins = kwargs['nbins']
1403+
if self._nbins != 'auto':
1404+
self._nbins = int(self._nbins)
13971405
if 'trim' in kwargs:
13981406
self._trim = kwargs['trim']
13991407
if 'integer' in kwargs:
@@ -1422,6 +1430,8 @@ def set_params(self, **kwargs):
14221430

14231431
def bin_boundaries(self, vmin, vmax):
14241432
nbins = self._nbins
1433+
if nbins == 'auto':
1434+
nbins = self.axis.get_tick_space()
14251435
scale, offset = scale_range(vmin, vmax, nbins)
14261436
if self._integer:
14271437
scale = max(1, scale)
@@ -1907,7 +1917,11 @@ def tick_values(self, vmin, vmax):
19071917

19081918
class AutoLocator(MaxNLocator):
19091919
def __init__(self):
1910-
MaxNLocator.__init__(self, nbins=9, steps=[1, 2, 5, 10])
1920+
if rcParams['_internal.classic_mode']:
1921+
nbins = 9
1922+
else:
1923+
nbins = 'auto'
1924+
MaxNLocator.__init__(self, nbins=nbins, steps=[1, 2, 5, 10])
19111925

19121926

19131927
class AutoMinorLocator(Locator):

0 commit comments

Comments
 (0)