1"""
2Tick locating and formatting
3============================
4
5This module contains classes to support completely configurable tick
6locating and formatting. Although the locators know nothing about major
7or minor ticks, they are used by the Axis class to support major and
8minor tick locating and formatting. Generic tick locators and
9formatters are provided, as well as domain specific custom ones.
10
11Default Formatter
12-----------------
13
14The default formatter identifies when the x-data being plotted is a
15small range on top of a large offset. To reduce the chances that the
16ticklabels overlap, the ticks are labeled as deltas from a fixed offset.
17For example::
18
19   ax.plot(np.arange(2000, 2010), range(10))
20
21will have tick of 0-9 with an offset of +2e3. If this is not desired
22turn off the use of the offset on the default formatter::
23
24   ax.get_xaxis().get_major_formatter().set_useOffset(False)
25
26set the rcParam ``axes.formatter.useoffset=False`` to turn it off
27globally, or set a different formatter.
28
29Tick locating
30-------------
31
32The Locator class is the base class for all tick locators. The locators
33handle autoscaling of the view limits based on the data limits, and the
34choosing of tick locations. A useful semi-automatic tick locator is
35`MultipleLocator`. It is initialized with a base, e.g., 10, and it picks
36axis limits and ticks that are multiples of that base.
37
38The Locator subclasses defined here are
39
40:class:`AutoLocator`
41    `MaxNLocator` with simple defaults.  This is the default tick locator for
42    most plotting.
43
44:class:`MaxNLocator`
45    Finds up to a max number of intervals with ticks at nice locations.
46
47:class:`LinearLocator`
48    Space ticks evenly from min to max.
49
50:class:`LogLocator`
51    Space ticks logarithmically from min to max.
52
53:class:`MultipleLocator`
54    Ticks and range are a multiple of base; either integer or float.
55
56:class:`FixedLocator`
57    Tick locations are fixed.
58
59:class:`IndexLocator`
60    Locator for index plots (e.g., where ``x = range(len(y))``).
61
62:class:`NullLocator`
63    No ticks.
64
65:class:`SymmetricalLogLocator`
66    Locator for use with with the symlog norm; works like `LogLocator` for the
67    part outside of the threshold and adds 0 if inside the limits.
68
69:class:`LogitLocator`
70    Locator for logit scaling.
71
72:class:`OldAutoLocator`
73    Choose a `MultipleLocator` and dynamically reassign it for intelligent
74    ticking during navigation.
75
76:class:`AutoMinorLocator`
77    Locator for minor ticks when the axis is linear and the
78    major ticks are uniformly spaced.  Subdivides the major
79    tick interval into a specified number of minor intervals,
80    defaulting to 4 or 5 depending on the major interval.
81
82
83There are a number of locators specialized for date locations - see
84the `dates` module.
85
86You can define your own locator by deriving from Locator. You must
87override the ``__call__`` method, which returns a sequence of locations,
88and you will probably want to override the autoscale method to set the
89view limits from the data limits.
90
91If you want to override the default locator, use one of the above or a custom
92locator and pass it to the x or y axis instance. The relevant methods are::
93
94  ax.xaxis.set_major_locator(xmajor_locator)
95  ax.xaxis.set_minor_locator(xminor_locator)
96  ax.yaxis.set_major_locator(ymajor_locator)
97  ax.yaxis.set_minor_locator(yminor_locator)
98
99The default minor locator is `NullLocator`, i.e., no minor ticks on by default.
100
101Tick formatting
102---------------
103
104Tick formatting is controlled by classes derived from Formatter. The formatter
105operates on a single tick value and returns a string to the axis.
106
107:class:`NullFormatter`
108    No labels on the ticks.
109
110:class:`IndexFormatter`
111    Set the strings from a list of labels.
112
113:class:`FixedFormatter`
114    Set the strings manually for the labels.
115
116:class:`FuncFormatter`
117    User defined function sets the labels.
118
119:class:`StrMethodFormatter`
120    Use string `format` method.
121
122:class:`FormatStrFormatter`
123    Use an old-style sprintf format string.
124
125:class:`ScalarFormatter`
126    Default formatter for scalars: autopick the format string.
127
128:class:`LogFormatter`
129    Formatter for log axes.
130
131:class:`LogFormatterExponent`
132    Format values for log axis using ``exponent = log_base(value)``.
133
134:class:`LogFormatterMathtext`
135    Format values for log axis using ``exponent = log_base(value)``
136    using Math text.
137
138:class:`LogFormatterSciNotation`
139    Format values for log axis using scientific notation.
140
141:class:`LogitFormatter`
142    Probability formatter.
143
144:class:`EngFormatter`
145    Format labels in engineering notation
146
147:class:`PercentFormatter`
148    Format labels as a percentage
149
150You can derive your own formatter from the Formatter base class by
151simply overriding the ``__call__`` method. The formatter class has
152access to the axis view and data limits.
153
154To control the major and minor tick label formats, use one of the
155following methods::
156
157  ax.xaxis.set_major_formatter(xmajor_formatter)
158  ax.xaxis.set_minor_formatter(xminor_formatter)
159  ax.yaxis.set_major_formatter(ymajor_formatter)
160  ax.yaxis.set_minor_formatter(yminor_formatter)
161
162See :doc:`/gallery/ticks_and_spines/major_minor_demo` for an
163example of setting major and minor ticks. See the :mod:`matplotlib.dates`
164module for more information and examples of using date locators and formatters.
165"""
166
167from __future__ import (absolute_import, division, print_function,
168                        unicode_literals)
169
170import six
171
172import itertools
173import locale
174import math
175import numpy as np
176from matplotlib import rcParams
177from matplotlib import cbook
178from matplotlib import transforms as mtransforms
179from matplotlib.cbook import mplDeprecation
180
181import warnings
182
183
184__all__ = ('TickHelper', 'Formatter', 'FixedFormatter',
185           'NullFormatter', 'FuncFormatter', 'FormatStrFormatter',
186           'StrMethodFormatter', 'ScalarFormatter', 'LogFormatter',
187           'LogFormatterExponent', 'LogFormatterMathtext',
188           'IndexFormatter', 'LogFormatterSciNotation',
189           'LogitFormatter', 'EngFormatter', 'PercentFormatter',
190           'Locator', 'IndexLocator', 'FixedLocator', 'NullLocator',
191           'LinearLocator', 'LogLocator', 'AutoLocator',
192           'MultipleLocator', 'MaxNLocator', 'AutoMinorLocator',
193           'SymmetricalLogLocator', 'LogitLocator')
194
195
196if six.PY3:
197    long = int
198
199
200# Work around numpy/numpy#6127.
201def _divmod(x, y):
202    if isinstance(x, np.generic):
203        x = x.item()
204    if isinstance(y, np.generic):
205        y = y.item()
206    return six.moves.builtins.divmod(x, y)
207
208
209def _mathdefault(s):
210    return '\\mathdefault{%s}' % s
211
212
213class _DummyAxis(object):
214    def __init__(self, minpos=0):
215        self.dataLim = mtransforms.Bbox.unit()
216        self.viewLim = mtransforms.Bbox.unit()
217        self._minpos = minpos
218
219    def get_view_interval(self):
220        return self.viewLim.intervalx
221
222    def set_view_interval(self, vmin, vmax):
223        self.viewLim.intervalx = vmin, vmax
224
225    def get_minpos(self):
226        return self._minpos
227
228    def get_data_interval(self):
229        return self.dataLim.intervalx
230
231    def set_data_interval(self, vmin, vmax):
232        self.dataLim.intervalx = vmin, vmax
233
234    def get_tick_space(self):
235        # Just use the long-standing default of nbins==9
236        return 9
237
238
239class TickHelper(object):
240    axis = None
241
242    def set_axis(self, axis):
243        self.axis = axis
244
245    def create_dummy_axis(self, **kwargs):
246        if self.axis is None:
247            self.axis = _DummyAxis(**kwargs)
248
249    def set_view_interval(self, vmin, vmax):
250        self.axis.set_view_interval(vmin, vmax)
251
252    def set_data_interval(self, vmin, vmax):
253        self.axis.set_data_interval(vmin, vmax)
254
255    def set_bounds(self, vmin, vmax):
256        self.set_view_interval(vmin, vmax)
257        self.set_data_interval(vmin, vmax)
258
259
260class Formatter(TickHelper):
261    """
262    Create a string based on a tick value and location.
263    """
264    # some classes want to see all the locs to help format
265    # individual ones
266    locs = []
267
268    def __call__(self, x, pos=None):
269        """
270        Return the format for tick value *x* at position pos.
271        ``pos=None`` indicates an unspecified location.
272        """
273        raise NotImplementedError('Derived must override')
274
275    def format_data(self, value):
276        """
277        Returns the full string representation of the value with the
278        position unspecified.
279        """
280        return self.__call__(value)
281
282    def format_data_short(self, value):
283        """
284        Return a short string version of the tick value.
285
286        Defaults to the position-independent long value.
287        """
288        return self.format_data(value)
289
290    def get_offset(self):
291        return ''
292
293    def set_locs(self, locs):
294        self.locs = locs
295
296    def fix_minus(self, s):
297        """
298        Some classes may want to replace a hyphen for minus with the
299        proper unicode symbol (U+2212) for typographical correctness.
300        The default is to not replace it.
301
302        Note, if you use this method, e.g., in :meth:`format_data` or
303        call, you probably don't want to use it for
304        :meth:`format_data_short` since the toolbar uses this for
305        interactive coord reporting and I doubt we can expect GUIs
306        across platforms will handle the unicode correctly.  So for
307        now the classes that override :meth:`fix_minus` should have an
308        explicit :meth:`format_data_short` method
309        """
310        return s
311
312
313class IndexFormatter(Formatter):
314    """
315    Format the position x to the nearest i-th label where i=int(x+0.5)
316    """
317    def __init__(self, labels):
318        self.labels = labels
319        self.n = len(labels)
320
321    def __call__(self, x, pos=None):
322        """
323        Return the format for tick value `x` at position pos.
324
325        The position is ignored and the value is rounded to the nearest
326        integer, which is used to look up the label.
327        """
328        i = int(x + 0.5)
329        if i < 0 or i >= self.n:
330            return ''
331        else:
332            return self.labels[i]
333
334
335class NullFormatter(Formatter):
336    """
337    Always return the empty string.
338    """
339    def __call__(self, x, pos=None):
340        """
341        Returns an empty string for all inputs.
342        """
343        return ''
344
345
346class FixedFormatter(Formatter):
347    """
348    Return fixed strings for tick labels based only on position, not
349    value.
350    """
351    def __init__(self, seq):
352        """
353        Set the sequence of strings that will be used for labels.
354        """
355        self.seq = seq
356        self.offset_string = ''
357
358    def __call__(self, x, pos=None):
359        """
360        Returns the label that matches the position regardless of the
361        value.
362
363        For positions ``pos < len(seq)``, return `seq[i]` regardless of
364        `x`. Otherwise return empty string. `seq` is the sequence of
365        strings that this object was initialized with.
366        """
367        if pos is None or pos >= len(self.seq):
368            return ''
369        else:
370            return self.seq[pos]
371
372    def get_offset(self):
373        return self.offset_string
374
375    def set_offset_string(self, ofs):
376        self.offset_string = ofs
377
378
379class FuncFormatter(Formatter):
380    """
381    Use a user-defined function for formatting.
382
383    The function should take in two inputs (a tick value ``x`` and a
384    position ``pos``), and return a string containing the corresponding
385    tick label.
386    """
387    def __init__(self, func):
388        self.func = func
389
390    def __call__(self, x, pos=None):
391        """
392        Return the value of the user defined function.
393
394        `x` and `pos` are passed through as-is.
395        """
396        return self.func(x, pos)
397
398
399class FormatStrFormatter(Formatter):
400    """
401    Use an old-style ('%' operator) format string to format the tick.
402
403    The format string should have a single variable format (%) in it.
404    It will be applied to the value (not the position) of the tick.
405    """
406    def __init__(self, fmt):
407        self.fmt = fmt
408
409    def __call__(self, x, pos=None):
410        """
411        Return the formatted label string.
412
413        Only the value `x` is formatted. The position is ignored.
414        """
415        return self.fmt % x
416
417
418class StrMethodFormatter(Formatter):
419    """
420    Use a new-style format string (as used by `str.format()`)
421    to format the tick.
422
423    The field used for the value must be labeled `x` and the field used
424    for the position must be labeled `pos`.
425    """
426    def __init__(self, fmt):
427        self.fmt = fmt
428
429    def __call__(self, x, pos=None):
430        """
431        Return the formatted label string.
432
433        `x` and `pos` are passed to `str.format` as keyword arguments
434        with those exact names.
435        """
436        return self.fmt.format(x=x, pos=pos)
437
438
439class OldScalarFormatter(Formatter):
440    """
441    Tick location is a plain old number.
442    """
443
444    def __call__(self, x, pos=None):
445        """
446        Return the format for tick val `x` based on the width of the
447        axis.
448
449        The position `pos` is ignored.
450        """
451        xmin, xmax = self.axis.get_view_interval()
452        d = abs(xmax - xmin)
453
454        return self.pprint_val(x, d)
455
456    def pprint_val(self, x, d):
457        """
458        Formats the value `x` based on the size of the axis range `d`.
459        """
460        #if the number is not too big and it's an int, format it as an
461        #int
462        if abs(x) < 1e4 and x == int(x):
463            return '%d' % x
464
465        if d < 1e-2:
466            fmt = '%1.3e'
467        elif d < 1e-1:
468            fmt = '%1.3f'
469        elif d > 1e5:
470            fmt = '%1.1e'
471        elif d > 10:
472            fmt = '%1.1f'
473        elif d > 1:
474            fmt = '%1.2f'
475        else:
476            fmt = '%1.3f'
477        s = fmt % x
478        tup = s.split('e')
479        if len(tup) == 2:
480            mantissa = tup[0].rstrip('0').rstrip('.')
481            sign = tup[1][0].replace('+', '')
482            exponent = tup[1][1:].lstrip('0')
483            s = '%se%s%s' % (mantissa, sign, exponent)
484        else:
485            s = s.rstrip('0').rstrip('.')
486        return s
487
488
489class ScalarFormatter(Formatter):
490    """
491    Format tick values as a number.
492
493    Tick value is interpreted as a plain old number. If
494    ``useOffset==True`` and the data range is much smaller than the data
495    average, then an offset will be determined such that the tick labels
496    are meaningful. Scientific notation is used for ``data < 10^-n`` or
497    ``data >= 10^m``, where ``n`` and ``m`` are the power limits set
498    using ``set_powerlimits((n,m))``. The defaults for these are
499    controlled by the ``axes.formatter.limits`` rc parameter.
500    """
501    def __init__(self, useOffset=None, useMathText=None, useLocale=None):
502        # useOffset allows plotting small data ranges with large offsets: for
503        # example: [1+1e-9,1+2e-9,1+3e-9] useMathText will render the offset
504        # and scientific notation in mathtext
505
506        if useOffset is None:
507            useOffset = rcParams['axes.formatter.useoffset']
508        self._offset_threshold = rcParams['axes.formatter.offset_threshold']
509        self.set_useOffset(useOffset)
510        self._usetex = rcParams['text.usetex']
511        if useMathText is None:
512            useMathText = rcParams['axes.formatter.use_mathtext']
513        self.set_useMathText(useMathText)
514        self.orderOfMagnitude = 0
515        self.format = ''
516        self._scientific = True
517        self._powerlimits = rcParams['axes.formatter.limits']
518        if useLocale is None:
519            useLocale = rcParams['axes.formatter.use_locale']
520        self._useLocale = useLocale
521
522    def get_useOffset(self):
523        return self._useOffset
524
525    def set_useOffset(self, val):
526        if val in [True, False]:
527            self.offset = 0
528            self._useOffset = val
529        else:
530            self._useOffset = False
531            self.offset = val
532
533    useOffset = property(fget=get_useOffset, fset=set_useOffset)
534
535    def get_useLocale(self):
536        return self._useLocale
537
538    def set_useLocale(self, val):
539        if val is None:
540            self._useLocale = rcParams['axes.formatter.use_locale']
541        else:
542            self._useLocale = val
543
544    useLocale = property(fget=get_useLocale, fset=set_useLocale)
545
546    def get_useMathText(self):
547        return self._useMathText
548
549    def set_useMathText(self, val):
550        if val is None:
551            self._useMathText = rcParams['axes.formatter.use_mathtext']
552        else:
553            self._useMathText = val
554
555    useMathText = property(fget=get_useMathText, fset=set_useMathText)
556
557    def fix_minus(self, s):
558        """
559        Replace hyphens with a unicode minus.
560        """
561        if rcParams['text.usetex'] or not rcParams['axes.unicode_minus']:
562            return s
563        else:
564            return s.replace('-', '\N{MINUS SIGN}')
565
566    def __call__(self, x, pos=None):
567        """
568        Return the format for tick value `x` at position `pos`.
569        """
570        if len(self.locs) == 0:
571            return ''
572        else:
573            s = self.pprint_val(x)
574            return self.fix_minus(s)
575
576    def set_scientific(self, b):
577        """
578        Turn scientific notation on or off.
579
580        .. seealso:: Method :meth:`set_powerlimits`
581        """
582        self._scientific = bool(b)
583
584    def set_powerlimits(self, lims):
585        """
586        Sets size thresholds for scientific notation.
587
588        Parameters
589        ----------
590        lims : (min_exp, max_exp)
591            A tuple containing the powers of 10 that determine the switchover
592            threshold. Numbers below ``10**min_exp`` and above ``10**max_exp``
593            will be displayed in scientific notation.
594
595            For example, ``formatter.set_powerlimits((-3, 4))`` sets the
596            pre-2007 default in which scientific notation is used for
597            numbers less than 1e-3 or greater than 1e4.
598
599        .. seealso:: Method :meth:`set_scientific`
600        """
601        if len(lims) != 2:
602            raise ValueError("'lims' must be a sequence of length 2")
603        self._powerlimits = lims
604
605    def format_data_short(self, value):
606        """
607        Return a short formatted string representation of a number.
608        """
609        if self._useLocale:
610            return locale.format_string('%-12g', (value,))
611        else:
612            return '%-12g' % value
613
614    def format_data(self, value):
615        """
616        Return a formatted string representation of a number.
617        """
618        if self._useLocale:
619            s = locale.format_string('%1.10e', (value,))
620        else:
621            s = '%1.10e' % value
622        s = self._formatSciNotation(s)
623        return self.fix_minus(s)
624
625    def get_offset(self):
626        """
627        Return scientific notation, plus offset.
628        """
629        if len(self.locs) == 0:
630            return ''
631        s = ''
632        if self.orderOfMagnitude or self.offset:
633            offsetStr = ''
634            sciNotStr = ''
635            if self.offset:
636                offsetStr = self.format_data(self.offset)
637                if self.offset > 0:
638                    offsetStr = '+' + offsetStr
639            if self.orderOfMagnitude:
640                if self._usetex or self._useMathText:
641                    sciNotStr = self.format_data(10 ** self.orderOfMagnitude)
642                else:
643                    sciNotStr = '1e%d' % self.orderOfMagnitude
644            if self._useMathText:
645                if sciNotStr != '':
646                    sciNotStr = r'\times%s' % _mathdefault(sciNotStr)
647                s = ''.join(('$', sciNotStr, _mathdefault(offsetStr), '$'))
648            elif self._usetex:
649                if sciNotStr != '':
650                    sciNotStr = r'\times%s' % sciNotStr
651                s = ''.join(('$', sciNotStr, offsetStr, '$'))
652            else:
653                s = ''.join((sciNotStr, offsetStr))
654
655        return self.fix_minus(s)
656
657    def set_locs(self, locs):
658        """
659        Set the locations of the ticks.
660        """
661        self.locs = locs
662        if len(self.locs) > 0:
663            vmin, vmax = self.axis.get_view_interval()
664            d = abs(vmax - vmin)
665            if self._useOffset:
666                self._compute_offset()
667            self._set_orderOfMagnitude(d)
668            self._set_format(vmin, vmax)
669
670    def _compute_offset(self):
671        locs = self.locs
672        if locs is None or not len(locs):
673            self.offset = 0
674            return
675        # Restrict to visible ticks.
676        vmin, vmax = sorted(self.axis.get_view_interval())
677        locs = np.asarray(locs)
678        locs = locs[(vmin <= locs) & (locs <= vmax)]
679        if not len(locs):
680            self.offset = 0
681            return
682        lmin, lmax = locs.min(), locs.max()
683        # Only use offset if there are at least two ticks and every tick has
684        # the same sign.
685        if lmin == lmax or lmin <= 0 <= lmax:
686            self.offset = 0
687            return
688        # min, max comparing absolute values (we want division to round towards
689        # zero so we work on absolute values).
690        abs_min, abs_max = sorted([abs(float(lmin)), abs(float(lmax))])
691        sign = math.copysign(1, lmin)
692        # What is the smallest power of ten such that abs_min and abs_max are
693        # equal up to that precision?
694        # Note: Internally using oom instead of 10 ** oom avoids some numerical
695        # accuracy issues.
696        oom_max = np.ceil(math.log10(abs_max))
697        oom = 1 + next(oom for oom in itertools.count(oom_max, -1)
698                       if abs_min // 10 ** oom != abs_max // 10 ** oom)
699        if (abs_max - abs_min) / 10 ** oom <= 1e-2:
700            # Handle the case of straddling a multiple of a large power of ten
701            # (relative to the span).
702            # What is the smallest power of ten such that abs_min and abs_max
703            # are no more than 1 apart at that precision?
704            oom = 1 + next(oom for oom in itertools.count(oom_max, -1)
705                           if abs_max // 10 ** oom - abs_min // 10 ** oom > 1)
706        # Only use offset if it saves at least _offset_threshold digits.
707        n = self._offset_threshold - 1
708        self.offset = (sign * (abs_max // 10 ** oom) * 10 ** oom
709                       if abs_max // 10 ** oom >= 10**n
710                       else 0)
711
712    def _set_orderOfMagnitude(self, range):
713        # if scientific notation is to be used, find the appropriate exponent
714        # if using an numerical offset, find the exponent after applying the
715        # offset
716        if not self._scientific:
717            self.orderOfMagnitude = 0
718            return
719        locs = np.abs(self.locs)
720        if self.offset:
721            oom = math.floor(math.log10(range))
722        else:
723            if locs[0] > locs[-1]:
724                val = locs[0]
725            else:
726                val = locs[-1]
727            if val == 0:
728                oom = 0
729            else:
730                oom = math.floor(math.log10(val))
731        if oom <= self._powerlimits[0]:
732            self.orderOfMagnitude = oom
733        elif oom >= self._powerlimits[1]:
734            self.orderOfMagnitude = oom
735        else:
736            self.orderOfMagnitude = 0
737
738    def _set_format(self, vmin, vmax):
739        # set the format string to format all the ticklabels
740        if len(self.locs) < 2:
741            # Temporarily augment the locations with the axis end points.
742            _locs = list(self.locs) + [vmin, vmax]
743        else:
744            _locs = self.locs
745        locs = (np.asarray(_locs) - self.offset) / 10. ** self.orderOfMagnitude
746        loc_range = np.ptp(locs)
747        # Curvilinear coordinates can yield two identical points.
748        if loc_range == 0:
749            loc_range = np.max(np.abs(locs))
750        # Both points might be zero.
751        if loc_range == 0:
752            loc_range = 1
753        if len(self.locs) < 2:
754            # We needed the end points only for the loc_range calculation.
755            locs = locs[:-2]
756        loc_range_oom = int(math.floor(math.log10(loc_range)))
757        # first estimate:
758        sigfigs = max(0, 3 - loc_range_oom)
759        # refined estimate:
760        thresh = 1e-3 * 10 ** loc_range_oom
761        while sigfigs >= 0:
762            if np.abs(locs - np.round(locs, decimals=sigfigs)).max() < thresh:
763                sigfigs -= 1
764            else:
765                break
766        sigfigs += 1
767        self.format = '%1.' + str(sigfigs) + 'f'
768        if self._usetex:
769            self.format = '$%s$' % self.format
770        elif self._useMathText:
771            self.format = '$%s$' % _mathdefault(self.format)
772
773    def pprint_val(self, x):
774        xp = (x - self.offset) / (10. ** self.orderOfMagnitude)
775        if np.abs(xp) < 1e-8:
776            xp = 0
777        if self._useLocale:
778            return locale.format_string(self.format, (xp,))
779        else:
780            return self.format % xp
781
782    def _formatSciNotation(self, s):
783        # transform 1e+004 into 1e4, for example
784        if self._useLocale:
785            decimal_point = locale.localeconv()['decimal_point']
786            positive_sign = locale.localeconv()['positive_sign']
787        else:
788            decimal_point = '.'
789            positive_sign = '+'
790        tup = s.split('e')
791        try:
792            significand = tup[0].rstrip('0').rstrip(decimal_point)
793            sign = tup[1][0].replace(positive_sign, '')
794            exponent = tup[1][1:].lstrip('0')
795            if self._useMathText or self._usetex:
796                if significand == '1' and exponent != '':
797                    # reformat 1x10^y as 10^y
798                    significand = ''
799                if exponent:
800                    exponent = '10^{%s%s}' % (sign, exponent)
801                if significand and exponent:
802                    return r'%s{\times}%s' % (significand, exponent)
803                else:
804                    return r'%s%s' % (significand, exponent)
805            else:
806                s = ('%se%s%s' % (significand, sign, exponent)).rstrip('e')
807                return s
808        except IndexError:
809            return s
810
811
812class LogFormatter(Formatter):
813    """
814    Base class for formatting ticks on a log or symlog scale.
815
816    It may be instantiated directly, or subclassed.
817
818    Parameters
819    ----------
820    base : float, optional, default: 10.
821        Base of the logarithm used in all calculations.
822
823    labelOnlyBase : bool, optional, default: False
824        If True, label ticks only at integer powers of base.
825        This is normally True for major ticks and False for
826        minor ticks.
827
828    minor_thresholds : (subset, all), optional, default: (1, 0.4)
829        If labelOnlyBase is False, these two numbers control
830        the labeling of ticks that are not at integer powers of
831        base; normally these are the minor ticks. The controlling
832        parameter is the log of the axis data range.  In the typical
833        case where base is 10 it is the number of decades spanned
834        by the axis, so we can call it 'numdec'. If ``numdec <= all``,
835        all minor ticks will be labeled.  If ``all < numdec <= subset``,
836        then only a subset of minor ticks will be labeled, so as to
837        avoid crowding. If ``numdec > subset`` then no minor ticks will
838        be labeled.
839
840    linthresh : None or float, optional, default: None
841        If a symmetric log scale is in use, its ``linthresh``
842        parameter must be supplied here.
843
844    Notes
845    -----
846    The `set_locs` method must be called to enable the subsetting
847    logic controlled by the ``minor_thresholds`` parameter.
848
849    In some cases such as the colorbar, there is no distinction between
850    major and minor ticks; the tick locations might be set manually,
851    or by a locator that puts ticks at integer powers of base and
852    at intermediate locations.  For this situation, disable the
853    minor_thresholds logic by using ``minor_thresholds=(np.inf, np.inf)``,
854    so that all ticks will be labeled.
855
856    To disable labeling of minor ticks when 'labelOnlyBase' is False,
857    use ``minor_thresholds=(0, 0)``.  This is the default for the
858    "classic" style.
859
860    Examples
861    --------
862    To label a subset of minor ticks when the view limits span up
863    to 2 decades, and all of the ticks when zoomed in to 0.5 decades
864    or less, use ``minor_thresholds=(2, 0.5)``.
865
866    To label all minor ticks when the view limits span up to 1.5
867    decades, use ``minor_thresholds=(1.5, 1.5)``.
868
869    """
870    def __init__(self, base=10.0, labelOnlyBase=False,
871                 minor_thresholds=None,
872                 linthresh=None):
873
874        self._base = float(base)
875        self.labelOnlyBase = labelOnlyBase
876        if minor_thresholds is None:
877            if rcParams['_internal.classic_mode']:
878                minor_thresholds = (0, 0)
879            else:
880                minor_thresholds = (1, 0.4)
881        self.minor_thresholds = minor_thresholds
882        self._sublabels = None
883        self._linthresh = linthresh
884
885    def base(self, base):
886        """
887        Change the *base* for labeling.
888
889        .. warning::
890           Should always match the base used for :class:`LogLocator`
891
892        """
893        self._base = base
894
895    def label_minor(self, labelOnlyBase):
896        """
897        Switch minor tick labeling on or off.
898
899        Parameters
900        ----------
901        labelOnlyBase : bool
902            If True, label ticks only at integer powers of base.
903
904        """
905        self.labelOnlyBase = labelOnlyBase
906
907    def set_locs(self, locs=None):
908        """
909        Use axis view limits to control which ticks are labeled.
910
911        The *locs* parameter is ignored in the present algorithm.
912
913        """
914        if np.isinf(self.minor_thresholds[0]):
915            self._sublabels = None
916            return
917
918        # Handle symlog case:
919        linthresh = self._linthresh
920        if linthresh is None:
921            try:
922                linthresh = self.axis.get_transform().linthresh
923            except AttributeError:
924                pass
925
926        vmin, vmax = self.axis.get_view_interval()
927        if vmin > vmax:
928            vmin, vmax = vmax, vmin
929
930        if linthresh is None and vmin <= 0:
931            # It's probably a colorbar with
932            # a format kwarg setting a LogFormatter in the manner
933            # that worked with 1.5.x, but that doesn't work now.
934            self._sublabels = set((1,))  # label powers of base
935            return
936
937        b = self._base
938        if linthresh is not None:  # symlog
939            # Only compute the number of decades in the logarithmic part of the
940            # axis
941            numdec = 0
942            if vmin < -linthresh:
943                rhs = min(vmax, -linthresh)
944                numdec += math.log(vmin / rhs) / math.log(b)
945            if vmax > linthresh:
946                lhs = max(vmin, linthresh)
947                numdec += math.log(vmax / lhs) / math.log(b)
948        else:
949            vmin = math.log(vmin) / math.log(b)
950            vmax = math.log(vmax) / math.log(b)
951            numdec = abs(vmax - vmin)
952
953        if numdec > self.minor_thresholds[0]:
954            # Label only bases
955            self._sublabels = {1}
956        elif numdec > self.minor_thresholds[1]:
957            # Add labels between bases at log-spaced coefficients;
958            # include base powers in case the locations include
959            # "major" and "minor" points, as in colorbar.
960            c = np.logspace(0, 1, int(b)//2 + 1, base=b)
961            self._sublabels = set(np.round(c))
962            # For base 10, this yields (1, 2, 3, 4, 6, 10).
963        else:
964            # Label all integer multiples of base**n.
965            self._sublabels = set(np.arange(1, b + 1))
966
967    def _num_to_string(self, x, vmin, vmax):
968        if x > 10000:
969            s = '%1.0e' % x
970        elif x < 1:
971            s = '%1.0e' % x
972        else:
973            s = self.pprint_val(x, vmax - vmin)
974        return s
975
976    def __call__(self, x, pos=None):
977        """
978        Return the format for tick val *x*.
979        """
980        if x == 0.0:  # Symlog
981            return '0'
982
983        x = abs(x)
984        b = self._base
985        # only label the decades
986        fx = math.log(x) / math.log(b)
987        is_x_decade = is_close_to_int(fx)
988        exponent = np.round(fx) if is_x_decade else np.floor(fx)
989        coeff = np.round(x / b ** exponent)
990
991        if self.labelOnlyBase and not is_x_decade:
992            return ''
993        if self._sublabels is not None and coeff not in self._sublabels:
994            return ''
995
996        vmin, vmax = self.axis.get_view_interval()
997        vmin, vmax = mtransforms.nonsingular(vmin, vmax, expander=0.05)
998        s = self._num_to_string(x, vmin, vmax)
999        return self.fix_minus(s)
1000
1001    def format_data(self, value):
1002        b = self.labelOnlyBase
1003        self.labelOnlyBase = False
1004        value = cbook.strip_math(self.__call__(value))
1005        self.labelOnlyBase = b
1006        return value
1007
1008    def format_data_short(self, value):
1009        """
1010        Return a short formatted string representation of a number.
1011        """
1012        return '%-12g' % value
1013
1014    def pprint_val(self, x, d):
1015        #if the number is not too big and it's an int, format it as an
1016        #int
1017        if abs(x) < 1e4 and x == int(x):
1018            return '%d' % x
1019
1020        if d < 1e-2:
1021            fmt = '%1.3e'
1022        elif d < 1e-1:
1023            fmt = '%1.3f'
1024        elif d > 1e5:
1025            fmt = '%1.1e'
1026        elif d > 10:
1027            fmt = '%1.1f'
1028        elif d > 1:
1029            fmt = '%1.2f'
1030        else:
1031            fmt = '%1.3f'
1032        s = fmt % x
1033
1034        tup = s.split('e')
1035        if len(tup) == 2:
1036            mantissa = tup[0].rstrip('0').rstrip('.')
1037            exponent = int(tup[1])
1038            if exponent:
1039                s = '%se%d' % (mantissa, exponent)
1040            else:
1041                s = mantissa
1042        else:
1043            s = s.rstrip('0').rstrip('.')
1044        return s
1045
1046
1047class LogFormatterExponent(LogFormatter):
1048    """
1049    Format values for log axis using ``exponent = log_base(value)``.
1050    """
1051    def _num_to_string(self, x, vmin, vmax):
1052        fx = math.log(x) / math.log(self._base)
1053        if abs(fx) > 10000:
1054            s = '%1.0g' % fx
1055        elif abs(fx) < 1:
1056            s = '%1.0g' % fx
1057        else:
1058            fd = math.log(vmax - vmin) / math.log(self._base)
1059            s = self.pprint_val(fx, fd)
1060        return s
1061
1062
1063class LogFormatterMathtext(LogFormatter):
1064    """
1065    Format values for log axis using ``exponent = log_base(value)``.
1066    """
1067
1068    def _non_decade_format(self, sign_string, base, fx, usetex):
1069        'Return string for non-decade locations'
1070        if usetex:
1071            return (r'$%s%s^{%.2f}$') % (sign_string, base, fx)
1072        else:
1073            return ('$%s$' % _mathdefault('%s%s^{%.2f}' %
1074                                          (sign_string, base, fx)))
1075
1076    def __call__(self, x, pos=None):
1077        """
1078        Return the format for tick value *x*.
1079
1080        The position *pos* is ignored.
1081        """
1082        usetex = rcParams['text.usetex']
1083        min_exp = rcParams['axes.formatter.min_exponent']
1084
1085        if x == 0:  # Symlog
1086            if usetex:
1087                return '$0$'
1088            else:
1089                return '$%s$' % _mathdefault('0')
1090
1091        sign_string = '-' if x < 0 else ''
1092        x = abs(x)
1093        b = self._base
1094
1095        # only label the decades
1096        fx = math.log(x) / math.log(b)
1097        is_x_decade = is_close_to_int(fx)
1098        exponent = np.round(fx) if is_x_decade else np.floor(fx)
1099        coeff = np.round(x / b ** exponent)
1100        if is_x_decade:
1101            fx = nearest_long(fx)
1102
1103        if self.labelOnlyBase and not is_x_decade:
1104            return ''
1105        if self._sublabels is not None and coeff not in self._sublabels:
1106            return ''
1107
1108        # use string formatting of the base if it is not an integer
1109        if b % 1 == 0.0:
1110            base = '%d' % b
1111        else:
1112            base = '%s' % b
1113
1114        if np.abs(fx) < min_exp:
1115            if usetex:
1116                return r'${0}{1:g}$'.format(sign_string, x)
1117            else:
1118                return '${0}$'.format(_mathdefault(
1119                    '{0}{1:g}'.format(sign_string, x)))
1120        elif not is_x_decade:
1121            return self._non_decade_format(sign_string, base, fx, usetex)
1122        else:
1123            if usetex:
1124                return (r'$%s%s^{%d}$') % (sign_string,
1125                                           base,
1126                                           nearest_long(fx))
1127            else:
1128                return ('$%s$' % _mathdefault(
1129                    '%s%s^{%d}' %
1130                    (sign_string, base, nearest_long(fx))))
1131
1132
1133class LogFormatterSciNotation(LogFormatterMathtext):
1134    """
1135    Format values following scientific notation in a logarithmic axis.
1136    """
1137
1138    def _non_decade_format(self, sign_string, base, fx, usetex):
1139        'Return string for non-decade locations'
1140        b = float(base)
1141        exponent = math.floor(fx)
1142        coeff = b ** fx / b ** exponent
1143        if is_close_to_int(coeff):
1144            coeff = nearest_long(coeff)
1145        if usetex:
1146            return (r'$%s%g\times%s^{%d}$') % \
1147                                        (sign_string, coeff, base, exponent)
1148        else:
1149            return ('$%s$' % _mathdefault(r'%s%g\times%s^{%d}' %
1150                                        (sign_string, coeff, base, exponent)))
1151
1152
1153class LogitFormatter(Formatter):
1154    """
1155    Probability formatter (using Math text).
1156    """
1157    def __call__(self, x, pos=None):
1158        s = ''
1159        if 0.01 <= x <= 0.99:
1160            s = '{:.2f}'.format(x)
1161        elif x < 0.01:
1162            if is_decade(x):
1163                s = '$10^{{{:.0f}}}$'.format(np.log10(x))
1164            else:
1165                s = '${:.5f}$'.format(x)
1166        else:  # x > 0.99
1167            if is_decade(1-x):
1168                s = '$1-10^{{{:.0f}}}$'.format(np.log10(1-x))
1169            else:
1170                s = '$1-{:.5f}$'.format(1-x)
1171        return s
1172
1173    def format_data_short(self, value):
1174        'return a short formatted string representation of a number'
1175        return '%-12g' % value
1176
1177
1178class EngFormatter(Formatter):
1179    """
1180    Formats axis values using engineering prefixes to represent powers
1181    of 1000, plus a specified unit, e.g., 10 MHz instead of 1e7.
1182    """
1183
1184    # The SI engineering prefixes
1185    ENG_PREFIXES = {
1186        -24: "y",
1187        -21: "z",
1188        -18: "a",
1189        -15: "f",
1190        -12: "p",
1191         -9: "n",
1192         -6: "\N{GREEK SMALL LETTER MU}",
1193         -3: "m",
1194          0: "",
1195          3: "k",
1196          6: "M",
1197          9: "G",
1198         12: "T",
1199         15: "P",
1200         18: "E",
1201         21: "Z",
1202         24: "Y"
1203    }
1204
1205    def __init__(self, unit="", places=None, sep=" "):
1206        """
1207        Parameters
1208        ----------
1209        unit : str (default: "")
1210            Unit symbol to use, suitable for use with single-letter
1211            representations of powers of 1000. For example, 'Hz' or 'm'.
1212
1213        places : int (default: None)
1214            Precision with which to display the number, specified in
1215            digits after the decimal point (there will be between one
1216            and three digits before the decimal point). If it is None,
1217            the formatting falls back to the floating point format '%g',
1218            which displays up to 6 *significant* digits, i.e. the equivalent
1219            value for *places* varies between 0 and 5 (inclusive).
1220
1221        sep : str (default: " ")
1222            Separator used between the value and the prefix/unit. For
1223            example, one get '3.14 mV' if ``sep`` is " " (default) and
1224            '3.14mV' if ``sep`` is "". Besides the default behavior, some
1225            other useful options may be:
1226
1227            * ``sep=""`` to append directly the prefix/unit to the value;
1228            * ``sep="\\N{THIN SPACE}"`` (``U+2009``);
1229            * ``sep="\\N{NARROW NO-BREAK SPACE}"`` (``U+202F``);
1230            * ``sep="\\N{NO-BREAK SPACE}"`` (``U+00A0``).
1231        """
1232        self.unit = unit
1233        self.places = places
1234        self.sep = sep
1235
1236    def __call__(self, x, pos=None):
1237        s = "%s%s" % (self.format_eng(x), self.unit)
1238        # Remove the trailing separator when there is neither prefix nor unit
1239        if len(self.sep) > 0 and s.endswith(self.sep):
1240            s = s[:-len(self.sep)]
1241        return self.fix_minus(s)
1242
1243    def format_eng(self, num):
1244        """
1245        Formats a number in engineering notation, appending a letter
1246        representing the power of 1000 of the original number.
1247        Some examples:
1248
1249        >>> format_eng(0)       # for self.places = 0
1250        '0'
1251
1252        >>> format_eng(1000000) # for self.places = 1
1253        '1.0 M'
1254
1255        >>> format_eng("-1e-6") # for self.places = 2
1256        u'-1.00 \N{GREEK SMALL LETTER MU}'
1257
1258        `num` may be a numeric value or a string that can be converted
1259        to a numeric value with ``float(num)``.
1260        """
1261        if isinstance(num, six.string_types):
1262            warnings.warn(
1263                "Passing a string as *num* argument is deprecated since"
1264                "Matplotlib 2.1, and is expected to be removed in 2.3.",
1265                mplDeprecation)
1266
1267        dnum = float(num)
1268        sign = 1
1269        fmt = "g" if self.places is None else ".{:d}f".format(self.places)
1270
1271        if dnum < 0:
1272            sign = -1
1273            dnum = -dnum
1274
1275        if dnum != 0:
1276            pow10 = int(math.floor(math.log10(dnum) / 3) * 3)
1277        else:
1278            pow10 = 0
1279            # Force dnum to zero, to avoid inconsistencies like
1280            # format_eng(-0) = "0" and format_eng(0.0) = "0"
1281            # but format_eng(-0.0) = "-0.0"
1282            dnum = 0.0
1283
1284        pow10 = np.clip(pow10, min(self.ENG_PREFIXES), max(self.ENG_PREFIXES))
1285
1286        mant = sign * dnum / (10.0 ** pow10)
1287        # Taking care of the cases like 999.9..., which
1288        # may be rounded to 1000 instead of 1 k.  Beware
1289        # of the corner case of values that are beyond
1290        # the range of SI prefixes (i.e. > 'Y').
1291        _fmant = float("{mant:{fmt}}".format(mant=mant, fmt=fmt))
1292        if _fmant >= 1000 and pow10 != max(self.ENG_PREFIXES):
1293            mant /= 1000
1294            pow10 += 3
1295
1296        prefix = self.ENG_PREFIXES[int(pow10)]
1297
1298        formatted = "{mant:{fmt}}{sep}{prefix}".format(
1299            mant=mant, sep=self.sep, prefix=prefix, fmt=fmt)
1300
1301        return formatted
1302
1303
1304class PercentFormatter(Formatter):
1305    """
1306    Format numbers as a percentage.
1307
1308    Parameters
1309    ----------
1310    xmax : float
1311        Determines how the number is converted into a percentage.
1312        *xmax* is the data value that corresponds to 100%.
1313        Percentages are computed as ``x / xmax * 100``. So if the data is
1314        already scaled to be percentages, *xmax* will be 100. Another common
1315        situation is where `xmax` is 1.0.
1316
1317    decimals : None or int
1318        The number of decimal places to place after the point.
1319        If *None* (the default), the number will be computed automatically.
1320
1321    symbol : string or None
1322        A string that will be appended to the label. It may be
1323        *None* or empty to indicate that no symbol should be used. LaTeX
1324        special characters are escaped in *symbol* whenever latex mode is
1325        enabled, unless *is_latex* is *True*.
1326
1327    is_latex : bool
1328        If *False*, reserved LaTeX characters in *symbol* will be escaped.
1329    """
1330    def __init__(self, xmax=100, decimals=None, symbol='%', is_latex=False):
1331        self.xmax = xmax + 0.0
1332        self.decimals = decimals
1333        self._symbol = symbol
1334        self._is_latex = is_latex
1335
1336    def __call__(self, x, pos=None):
1337        """
1338        Formats the tick as a percentage with the appropriate scaling.
1339        """
1340        ax_min, ax_max = self.axis.get_view_interval()
1341        display_range = abs(ax_max - ax_min)
1342
1343        return self.fix_minus(self.format_pct(x, display_range))
1344
1345    def format_pct(self, x, display_range):
1346        """
1347        Formats the number as a percentage number with the correct
1348        number of decimals and adds the percent symbol, if any.
1349
1350        If `self.decimals` is `None`, the number of digits after the
1351        decimal point is set based on the `display_range` of the axis
1352        as follows:
1353
1354        +---------------+----------+------------------------+
1355        | display_range | decimals |          sample        |
1356        +---------------+----------+------------------------+
1357        | >50           |     0    | ``x = 34.5`` => 35%    |
1358        +---------------+----------+------------------------+
1359        | >5            |     1    | ``x = 34.5`` => 34.5%  |
1360        +---------------+----------+------------------------+
1361        | >0.5          |     2    | ``x = 34.5`` => 34.50% |
1362        +---------------+----------+------------------------+
1363        |      ...      |    ...   |          ...           |
1364        +---------------+----------+------------------------+
1365
1366        This method will not be very good for tiny axis ranges or
1367        extremely large ones. It assumes that the values on the chart
1368        are percentages displayed on a reasonable scale.
1369        """
1370        x = self.convert_to_pct(x)
1371        if self.decimals is None:
1372            # conversion works because display_range is a difference
1373            scaled_range = self.convert_to_pct(display_range)
1374            if scaled_range <= 0:
1375                decimals = 0
1376            else:
1377                # Luckily Python's built-in ceil rounds to +inf, not away from
1378                # zero. This is very important since the equation for decimals
1379                # starts out as `scaled_range > 0.5 * 10**(2 - decimals)`
1380                # and ends up with `decimals > 2 - log10(2 * scaled_range)`.
1381                decimals = math.ceil(2.0 - math.log10(2.0 * scaled_range))
1382                if decimals > 5:
1383                    decimals = 5
1384                elif decimals < 0:
1385                    decimals = 0
1386        else:
1387            decimals = self.decimals
1388        s = '{x:0.{decimals}f}'.format(x=x, decimals=int(decimals))
1389
1390        return s + self.symbol
1391
1392    def convert_to_pct(self, x):
1393        return 100.0 * (x / self.xmax)
1394
1395    @property
1396    def symbol(self):
1397        """
1398        The configured percent symbol as a string.
1399
1400        If LaTeX is enabled via :rc:`text.usetex`, the special characters
1401        ``{'#', '$', '%', '&', '~', '_', '^', '\\', '{', '}'}`` are
1402        automatically escaped in the string.
1403        """
1404        symbol = self._symbol
1405        if not symbol:
1406            symbol = ''
1407        elif rcParams['text.usetex'] and not self._is_latex:
1408            # Source: http://www.personal.ceu.hu/tex/specchar.htm
1409            # Backslash must be first for this to work correctly since
1410            # it keeps getting added in
1411            for spec in r'\#$%&~_^{}':
1412                symbol = symbol.replace(spec, '\\' + spec)
1413        return symbol
1414
1415    @symbol.setter
1416    def symbol(self, symbol):
1417        self._symbol = symbol
1418
1419
1420class Locator(TickHelper):
1421    """
1422    Determine the tick locations;
1423
1424    Note, you should not use the same locator between different
1425    :class:`~matplotlib.axis.Axis` because the locator stores references to
1426    the Axis data and view limits
1427    """
1428
1429    # Some automatic tick locators can generate so many ticks they
1430    # kill the machine when you try and render them.
1431    # This parameter is set to cause locators to raise an error if too
1432    # many ticks are generated.
1433    MAXTICKS = 1000
1434
1435    def tick_values(self, vmin, vmax):
1436        """
1437        Return the values of the located ticks given **vmin** and **vmax**.
1438
1439        .. note::
1440            To get tick locations with the vmin and vmax values defined
1441            automatically for the associated :attr:`axis` simply call
1442            the Locator instance::
1443
1444                >>> print((type(loc)))
1445                <type 'Locator'>
1446                >>> print((loc()))
1447                [1, 2, 3, 4]
1448
1449        """
1450        raise NotImplementedError('Derived must override')
1451
1452    def set_params(self, **kwargs):
1453        """
1454        Do nothing, and rase a warning. Any locator class not supporting the
1455        set_params() function will call this.
1456        """
1457        warnings.warn("'set_params()' not defined for locator of type " +
1458                      str(type(self)))
1459
1460    def __call__(self):
1461        """Return the locations of the ticks"""
1462        # note: some locators return data limits, other return view limits,
1463        # hence there is no *one* interface to call self.tick_values.
1464        raise NotImplementedError('Derived must override')
1465
1466    def raise_if_exceeds(self, locs):
1467        """raise a RuntimeError if Locator attempts to create more than
1468           MAXTICKS locs"""
1469        if len(locs) >= self.MAXTICKS:
1470            raise RuntimeError("Locator attempting to generate {} ticks from "
1471                               "{} to {}: exceeds Locator.MAXTICKS".format(
1472                                   len(locs), locs[0], locs[-1]))
1473        return locs
1474
1475    def view_limits(self, vmin, vmax):
1476        """
1477        select a scale for the range from vmin to vmax
1478
1479        Normally this method is overridden by subclasses to
1480        change locator behaviour.
1481        """
1482        return mtransforms.nonsingular(vmin, vmax)
1483
1484    def autoscale(self):
1485        """autoscale the view limits"""
1486        return self.view_limits(*self.axis.get_view_interval())
1487
1488    def pan(self, numsteps):
1489        """Pan numticks (can be positive or negative)"""
1490        ticks = self()
1491        numticks = len(ticks)
1492
1493        vmin, vmax = self.axis.get_view_interval()
1494        vmin, vmax = mtransforms.nonsingular(vmin, vmax, expander=0.05)
1495        if numticks > 2:
1496            step = numsteps * abs(ticks[0] - ticks[1])
1497        else:
1498            d = abs(vmax - vmin)
1499            step = numsteps * d / 6.
1500
1501        vmin += step
1502        vmax += step
1503        self.axis.set_view_interval(vmin, vmax, ignore=True)
1504
1505    def zoom(self, direction):
1506        "Zoom in/out on axis; if direction is >0 zoom in, else zoom out"
1507
1508        vmin, vmax = self.axis.get_view_interval()
1509        vmin, vmax = mtransforms.nonsingular(vmin, vmax, expander=0.05)
1510        interval = abs(vmax - vmin)
1511        step = 0.1 * interval * direction
1512        self.axis.set_view_interval(vmin + step, vmax - step, ignore=True)
1513
1514    def refresh(self):
1515        """refresh internal information based on current lim"""
1516        pass
1517
1518
1519class IndexLocator(Locator):
1520    """
1521    Place a tick on every multiple of some base number of points
1522    plotted, e.g., on every 5th point.  It is assumed that you are doing
1523    index plotting; i.e., the axis is 0, len(data).  This is mainly
1524    useful for x ticks.
1525    """
1526    def __init__(self, base, offset):
1527        'place ticks on the i-th data points where (i-offset)%base==0'
1528        self._base = base
1529        self.offset = offset
1530
1531    def set_params(self, base=None, offset=None):
1532        """Set parameters within this locator"""
1533        if base is not None:
1534            self._base = base
1535        if offset is not None:
1536            self.offset = offset
1537
1538    def __call__(self):
1539        """Return the locations of the ticks"""
1540        dmin, dmax = self.axis.get_data_interval()
1541        return self.tick_values(dmin, dmax)
1542
1543    def tick_values(self, vmin, vmax):
1544        return self.raise_if_exceeds(
1545            np.arange(vmin + self.offset, vmax + 1, self._base))
1546
1547
1548class FixedLocator(Locator):
1549    """
1550    Tick locations are fixed.  If nbins is not None,
1551    the array of possible positions will be subsampled to
1552    keep the number of ticks <= nbins +1.
1553    The subsampling will be done so as to include the smallest
1554    absolute value; for example, if zero is included in the
1555    array of possibilities, then it is guaranteed to be one of
1556    the chosen ticks.
1557    """
1558
1559    def __init__(self, locs, nbins=None):
1560        self.locs = np.asarray(locs)
1561        self.nbins = nbins
1562        if self.nbins is not None:
1563            self.nbins = max(self.nbins, 2)
1564
1565    def set_params(self, nbins=None):
1566        """Set parameters within this locator."""
1567        if nbins is not None:
1568            self.nbins = nbins
1569
1570    def __call__(self):
1571        return self.tick_values(None, None)
1572
1573    def tick_values(self, vmin, vmax):
1574        """"
1575        Return the locations of the ticks.
1576
1577        .. note::
1578
1579            Because the values are fixed, vmin and vmax are not used in this
1580            method.
1581
1582        """
1583        if self.nbins is None:
1584            return self.locs
1585        step = max(int(np.ceil(len(self.locs) / self.nbins)), 1)
1586        ticks = self.locs[::step]
1587        for i in range(1, step):
1588            ticks1 = self.locs[i::step]
1589            if np.abs(ticks1).min() < np.abs(ticks).min():
1590                ticks = ticks1
1591        return self.raise_if_exceeds(ticks)
1592
1593
1594class NullLocator(Locator):
1595    """
1596    No ticks
1597    """
1598
1599    def __call__(self):
1600        return self.tick_values(None, None)
1601
1602    def tick_values(self, vmin, vmax):
1603        """"
1604        Return the locations of the ticks.
1605
1606        .. note::
1607
1608            Because the values are Null, vmin and vmax are not used in this
1609            method.
1610        """
1611        return []
1612
1613
1614class LinearLocator(Locator):
1615    """
1616    Determine the tick locations
1617
1618    The first time this function is called it will try to set the
1619    number of ticks to make a nice tick partitioning.  Thereafter the
1620    number of ticks will be fixed so that interactive navigation will
1621    be nice
1622
1623    """
1624    def __init__(self, numticks=None, presets=None):
1625        """
1626        Use presets to set locs based on lom.  A dict mapping vmin, vmax->locs
1627        """
1628        self.numticks = numticks
1629        if presets is None:
1630            self.presets = {}
1631        else:
1632            self.presets = presets
1633
1634    def set_params(self, numticks=None, presets=None):
1635        """Set parameters within this locator."""
1636        if presets is not None:
1637            self.presets = presets
1638        if numticks is not None:
1639            self.numticks = numticks
1640
1641    def __call__(self):
1642        'Return the locations of the ticks'
1643        vmin, vmax = self.axis.get_view_interval()
1644        return self.tick_values(vmin, vmax)
1645
1646    def tick_values(self, vmin, vmax):
1647        vmin, vmax = mtransforms.nonsingular(vmin, vmax, expander=0.05)
1648        if vmax < vmin:
1649            vmin, vmax = vmax, vmin
1650
1651        if (vmin, vmax) in self.presets:
1652            return self.presets[(vmin, vmax)]
1653
1654        if self.numticks is None:
1655            self._set_numticks()
1656
1657        if self.numticks == 0:
1658            return []
1659        ticklocs = np.linspace(vmin, vmax, self.numticks)
1660
1661        return self.raise_if_exceeds(ticklocs)
1662
1663    def _set_numticks(self):
1664        self.numticks = 11  # todo; be smart here; this is just for dev
1665
1666    def view_limits(self, vmin, vmax):
1667        'Try to choose the view limits intelligently'
1668
1669        if vmax < vmin:
1670            vmin, vmax = vmax, vmin
1671
1672        if vmin == vmax:
1673            vmin -= 1
1674            vmax += 1
1675
1676        if rcParams['axes.autolimit_mode'] == 'round_numbers':
1677            exponent, remainder = _divmod(
1678                math.log10(vmax - vmin), math.log10(max(self.numticks - 1, 1)))
1679            exponent -= (remainder < .5)
1680            scale = max(self.numticks - 1, 1) ** (-exponent)
1681            vmin = math.floor(scale * vmin) / scale
1682            vmax = math.ceil(scale * vmax) / scale
1683
1684        return mtransforms.nonsingular(vmin, vmax)
1685
1686
1687def closeto(x, y):
1688    if abs(x - y) < 1e-10:
1689        return True
1690    else:
1691        return False
1692
1693
1694class Base(object):
1695    'this solution has some hacks to deal with floating point inaccuracies'
1696    def __init__(self, base):
1697        if base <= 0:
1698            raise ValueError("'base' must be positive")
1699        self._base = base
1700
1701    def lt(self, x):
1702        'return the largest multiple of base < x'
1703        d, m = _divmod(x, self._base)
1704        if closeto(m, 0) and not closeto(m / self._base, 1):
1705            return (d - 1) * self._base
1706        return d * self._base
1707
1708    def le(self, x):
1709        'return the largest multiple of base <= x'
1710        d, m = _divmod(x, self._base)
1711        if closeto(m / self._base, 1):  # was closeto(m, self._base)
1712            #looks like floating point error
1713            return (d + 1) * self._base
1714        return d * self._base
1715
1716    def gt(self, x):
1717        'return the smallest multiple of base > x'
1718        d, m = _divmod(x, self._base)
1719        if closeto(m / self._base, 1):
1720            #looks like floating point error
1721            return (d + 2) * self._base
1722        return (d + 1) * self._base
1723
1724    def ge(self, x):
1725        'return the smallest multiple of base >= x'
1726        d, m = _divmod(x, self._base)
1727        if closeto(m, 0) and not closeto(m / self._base, 1):
1728            return d * self._base
1729        return (d + 1) * self._base
1730
1731    def get_base(self):
1732        return self._base
1733
1734
1735class MultipleLocator(Locator):
1736    """
1737    Set a tick on every integer that is multiple of base in the
1738    view interval
1739    """
1740
1741    def __init__(self, base=1.0):
1742        self._base = Base(base)
1743
1744    def set_params(self, base):
1745        """Set parameters within this locator."""
1746        if base is not None:
1747            self._base = base
1748
1749    def __call__(self):
1750        'Return the locations of the ticks'
1751        vmin, vmax = self.axis.get_view_interval()
1752        return self.tick_values(vmin, vmax)
1753
1754    def tick_values(self, vmin, vmax):
1755        if vmax < vmin:
1756            vmin, vmax = vmax, vmin
1757        vmin = self._base.ge(vmin)
1758        base = self._base.get_base()
1759        n = (vmax - vmin + 0.001 * base) // base
1760        locs = vmin - base + np.arange(n + 3) * base
1761        return self.raise_if_exceeds(locs)
1762
1763    def view_limits(self, dmin, dmax):
1764        """
1765        Set the view limits to the nearest multiples of base that
1766        contain the data
1767        """
1768        if rcParams['axes.autolimit_mode'] == 'round_numbers':
1769            vmin = self._base.le(dmin)
1770            vmax = self._base.ge(dmax)
1771            if vmin == vmax:
1772                vmin -= 1
1773                vmax += 1
1774        else:
1775            vmin = dmin
1776            vmax = dmax
1777
1778        return mtransforms.nonsingular(vmin, vmax)
1779
1780
1781def scale_range(vmin, vmax, n=1, threshold=100):
1782    dv = abs(vmax - vmin)  # > 0 as nonsingular is called before.
1783    meanv = (vmax + vmin) / 2
1784    if abs(meanv) / dv < threshold:
1785        offset = 0
1786    else:
1787        offset = math.copysign(10 ** (math.log10(abs(meanv)) // 1), meanv)
1788    scale = 10 ** (math.log10(dv / n) // 1)
1789    return scale, offset
1790
1791
1792class MaxNLocator(Locator):
1793    """
1794    Select no more than N intervals at nice locations.
1795    """
1796    default_params = dict(nbins=10,
1797                          steps=None,
1798                          integer=False,
1799                          symmetric=False,
1800                          prune=None,
1801                          min_n_ticks=2)
1802
1803    def __init__(self, *args, **kwargs):
1804        """
1805        Keyword args:
1806
1807        *nbins*
1808            Maximum number of intervals; one less than max number of
1809            ticks.  If the string `'auto'`, the number of bins will be
1810            automatically determined based on the length of the axis.
1811
1812        *steps*
1813            Sequence of nice numbers starting with 1 and ending with 10;
1814            e.g., [1, 2, 4, 5, 10], where the values are acceptable
1815            tick multiples.  i.e. for the example, 20, 40, 60 would be
1816            an acceptable set of ticks, as would 0.4, 0.6, 0.8, because
1817            they are multiples of 2.  However, 30, 60, 90 would not
1818            be allowed because 3 does not appear in the list of steps.
1819
1820        *integer*
1821            If True, ticks will take only integer values, provided
1822            at least `min_n_ticks` integers are found within the
1823            view limits.
1824
1825        *symmetric*
1826            If True, autoscaling will result in a range symmetric
1827            about zero.
1828
1829        *prune*
1830            ['lower' | 'upper' | 'both' | None]
1831            Remove edge ticks -- useful for stacked or ganged plots where
1832            the upper tick of one axes overlaps with the lower tick of the
1833            axes above it, primarily when :rc:`axes.autolimit_mode` is
1834            ``'round_numbers'``.  If ``prune=='lower'``, the smallest tick will
1835            be removed.  If ``prune == 'upper'``, the largest tick will be
1836            removed.  If ``prune == 'both'``, the largest and smallest ticks
1837            will be removed.  If ``prune == None``, no ticks will be removed.
1838
1839        *min_n_ticks*
1840            Relax `nbins` and `integer` constraints if necessary to
1841            obtain this minimum number of ticks.
1842
1843        """
1844        if args:
1845            kwargs['nbins'] = args[0]
1846            if len(args) > 1:
1847                raise ValueError(
1848                    "Keywords are required for all arguments except 'nbins'")
1849        self.set_params(**self.default_params)
1850        self.set_params(**kwargs)
1851
1852    @staticmethod
1853    def _validate_steps(steps):
1854        if not np.iterable(steps):
1855            raise ValueError('steps argument must be a sequence of numbers '
1856                             'from 1 to 10')
1857        steps = np.asarray(steps)
1858        if np.any(np.diff(steps) <= 0):
1859            raise ValueError('steps argument must be uniformly increasing')
1860        if steps[-1] > 10 or steps[0] < 1:
1861            warnings.warn('Steps argument should be a sequence of numbers\n'
1862                          'increasing from 1 to 10, inclusive. Behavior with\n'
1863                          'values outside this range is undefined, and will\n'
1864                          'raise a ValueError in future versions of mpl.')
1865        if steps[0] != 1:
1866            steps = np.hstack((1, steps))
1867        if steps[-1] != 10:
1868            steps = np.hstack((steps, 10))
1869        return steps
1870
1871    @staticmethod
1872    def _staircase(steps):
1873        # Make an extended staircase within which the needed
1874        # step will be found.  This is probably much larger
1875        # than necessary.
1876        flights = (0.1 * steps[:-1], steps, 10 * steps[1])
1877        return np.hstack(flights)
1878
1879    def set_params(self, **kwargs):
1880        """Set parameters within this locator."""
1881        if 'nbins' in kwargs:
1882            self._nbins = kwargs['nbins']
1883            if self._nbins != 'auto':
1884                self._nbins = int(self._nbins)
1885        if 'symmetric' in kwargs:
1886            self._symmetric = kwargs['symmetric']
1887        if 'prune' in kwargs:
1888            prune = kwargs['prune']
1889            if prune is not None and prune not in ['upper', 'lower', 'both']:
1890                raise ValueError(
1891                    "prune must be 'upper', 'lower', 'both', or None")
1892            self._prune = prune
1893        if 'min_n_ticks' in kwargs:
1894            self._min_n_ticks = max(1, kwargs['min_n_ticks'])
1895        if 'steps' in kwargs:
1896            steps = kwargs['steps']
1897            if steps is None:
1898                self._steps = np.array([1, 1.5, 2, 2.5, 3, 4, 5, 6, 8, 10])
1899            else:
1900                self._steps = self._validate_steps(steps)
1901            self._extended_steps = self._staircase(self._steps)
1902        if 'integer' in kwargs:
1903            self._integer = kwargs['integer']
1904
1905    def _raw_ticks(self, vmin, vmax):
1906        if self._nbins == 'auto':
1907            if self.axis is not None:
1908                nbins = np.clip(self.axis.get_tick_space(),
1909                                max(1, self._min_n_ticks - 1), 9)
1910            else:
1911                nbins = 9
1912        else:
1913            nbins = self._nbins
1914
1915        scale, offset = scale_range(vmin, vmax, nbins)
1916        _vmin = vmin - offset
1917        _vmax = vmax - offset
1918        raw_step = (vmax - vmin) / nbins
1919        steps = self._extended_steps * scale
1920        if self._integer:
1921            # For steps > 1, keep only integer values.
1922            igood = (steps < 1) | (np.abs(steps - np.round(steps)) < 0.001)
1923            steps = steps[igood]
1924
1925        istep = np.nonzero(steps >= raw_step)[0][0]
1926
1927        # Classic round_numbers mode may require a larger step.
1928        if rcParams['axes.autolimit_mode'] == 'round_numbers':
1929            for istep in range(istep, len(steps)):
1930                step = steps[istep]
1931                best_vmin = (_vmin // step) * step
1932                best_vmax = best_vmin + step * nbins
1933                if (best_vmax >= _vmax):
1934                    break
1935
1936        # This is an upper limit; move to smaller steps if necessary.
1937        for i in range(istep):
1938            step = steps[istep - i]
1939            if (self._integer and
1940                    np.floor(_vmax) - np.ceil(_vmin) >= self._min_n_ticks - 1):
1941                step = max(1, step)
1942            best_vmin = (_vmin // step) * step
1943
1944            low = np.round(Base(step).le(_vmin - best_vmin) / step)
1945            high = np.round(Base(step).ge(_vmax - best_vmin) / step)
1946            ticks = np.arange(low, high + 1) * step + best_vmin + offset
1947            nticks = ((ticks <= vmax) & (ticks >= vmin)).sum()
1948            if nticks >= self._min_n_ticks:
1949                break
1950        return ticks
1951
1952    def __call__(self):
1953        vmin, vmax = self.axis.get_view_interval()
1954        return self.tick_values(vmin, vmax)
1955
1956    def tick_values(self, vmin, vmax):
1957        if self._symmetric:
1958            vmax = max(abs(vmin), abs(vmax))
1959            vmin = -vmax
1960        vmin, vmax = mtransforms.nonsingular(
1961            vmin, vmax, expander=1e-13, tiny=1e-14)
1962        locs = self._raw_ticks(vmin, vmax)
1963
1964        prune = self._prune
1965        if prune == 'lower':
1966            locs = locs[1:]
1967        elif prune == 'upper':
1968            locs = locs[:-1]
1969        elif prune == 'both':
1970            locs = locs[1:-1]
1971        return self.raise_if_exceeds(locs)
1972
1973    def view_limits(self, dmin, dmax):
1974        if self._symmetric:
1975            dmax = max(abs(dmin), abs(dmax))
1976            dmin = -dmax
1977
1978        dmin, dmax = mtransforms.nonsingular(
1979            dmin, dmax, expander=1e-12, tiny=1e-13)
1980
1981        if rcParams['axes.autolimit_mode'] == 'round_numbers':
1982            return self._raw_ticks(dmin, dmax)[[0, -1]]
1983        else:
1984            return dmin, dmax
1985
1986
1987def decade_down(x, base=10):
1988    'floor x to the nearest lower decade'
1989    if x == 0.0:
1990        return -base
1991    lx = np.floor(np.log(x) / np.log(base))
1992    return base ** lx
1993
1994
1995def decade_up(x, base=10):
1996    'ceil x to the nearest higher decade'
1997    if x == 0.0:
1998        return base
1999    lx = np.ceil(np.log(x) / np.log(base))
2000    return base ** lx
2001
2002
2003def nearest_long(x):
2004    if x == 0:
2005        return long(0)
2006    elif x > 0:
2007        return long(x + 0.5)
2008    else:
2009        return long(x - 0.5)
2010
2011
2012def is_decade(x, base=10):
2013    if not np.isfinite(x):
2014        return False
2015    if x == 0.0:
2016        return True
2017    lx = np.log(np.abs(x)) / np.log(base)
2018    return is_close_to_int(lx)
2019
2020
2021def is_close_to_int(x):
2022    if not np.isfinite(x):
2023        return False
2024    return abs(x - nearest_long(x)) < 1e-10
2025
2026
2027class LogLocator(Locator):
2028    """
2029    Determine the tick locations for log axes
2030    """
2031
2032    def __init__(self, base=10.0, subs=(1.0,), numdecs=4, numticks=None):
2033        """
2034        Place ticks on the locations : subs[j] * base**i
2035
2036        Parameters
2037        ----------
2038        subs : None, string, or sequence of float, optional, default (1.0,)
2039            Gives the multiples of integer powers of the base at which
2040            to place ticks.  The default places ticks only at
2041            integer powers of the base.
2042            The permitted string values are ``'auto'`` and ``'all'``,
2043            both of which use an algorithm based on the axis view
2044            limits to determine whether and how to put ticks between
2045            integer powers of the base.  With ``'auto'``, ticks are
2046            placed only between integer powers; with ``'all'``, the
2047            integer powers are included.  A value of None is
2048            equivalent to ``'auto'``.
2049
2050        """
2051        if numticks is None:
2052            if rcParams['_internal.classic_mode']:
2053                numticks = 15
2054            else:
2055                numticks = 'auto'
2056        self.base(base)
2057        self.subs(subs)
2058        self.numdecs = numdecs
2059        self.numticks = numticks
2060
2061    def set_params(self, base=None, subs=None, numdecs=None, numticks=None):
2062        """Set parameters within this locator."""
2063        if base is not None:
2064            self.base(base)
2065        if subs is not None:
2066            self.subs(subs)
2067        if numdecs is not None:
2068            self.numdecs = numdecs
2069        if numticks is not None:
2070            self.numticks = numticks
2071
2072    # FIXME: these base and subs functions are contrary to our
2073    # usual and desired API.
2074
2075    def base(self, base):
2076        """
2077        set the base of the log scaling (major tick every base**i, i integer)
2078        """
2079        self._base = float(base)
2080
2081    def subs(self, subs):
2082        """
2083        set the minor ticks for the log scaling every base**i*subs[j]
2084        """
2085        if subs is None:  # consistency with previous bad API
2086            self._subs = 'auto'
2087        elif isinstance(subs, six.string_types):
2088            if subs not in ('all', 'auto'):
2089                raise ValueError("A subs string must be 'all' or 'auto'; "
2090                                 "found '%s'." % subs)
2091            self._subs = subs
2092        else:
2093            self._subs = np.asarray(subs, dtype=float)
2094
2095    def __call__(self):
2096        'Return the locations of the ticks'
2097        vmin, vmax = self.axis.get_view_interval()
2098        return self.tick_values(vmin, vmax)
2099
2100    def tick_values(self, vmin, vmax):
2101        if self.numticks == 'auto':
2102            if self.axis is not None:
2103                numticks = np.clip(self.axis.get_tick_space(), 2, 9)
2104            else:
2105                numticks = 9
2106        else:
2107            numticks = self.numticks
2108
2109        b = self._base
2110        # dummy axis has no axes attribute
2111        if hasattr(self.axis, 'axes') and self.axis.axes.name == 'polar':
2112            vmax = math.ceil(math.log(vmax) / math.log(b))
2113            decades = np.arange(vmax - self.numdecs, vmax)
2114            ticklocs = b ** decades
2115
2116            return ticklocs
2117
2118        if vmin <= 0.0:
2119            if self.axis is not None:
2120                vmin = self.axis.get_minpos()
2121
2122            if vmin <= 0.0 or not np.isfinite(vmin):
2123                raise ValueError(
2124                    "Data has no positive values, and therefore can not be "
2125                    "log-scaled.")
2126
2127        vmin = math.log(vmin) / math.log(b)
2128        vmax = math.log(vmax) / math.log(b)
2129
2130        if vmax < vmin:
2131            vmin, vmax = vmax, vmin
2132
2133        numdec = math.floor(vmax) - math.ceil(vmin)
2134
2135        if isinstance(self._subs, six.string_types):
2136            _first = 2.0 if self._subs == 'auto' else 1.0
2137            if numdec > 10 or b < 3:
2138                if self._subs == 'auto':
2139                    return np.array([])  # no minor or major ticks
2140                else:
2141                    subs = np.array([1.0])  # major ticks
2142            else:
2143                subs = np.arange(_first, b)
2144        else:
2145            subs = self._subs
2146
2147        stride = 1
2148
2149        if rcParams['_internal.classic_mode']:
2150            # Leave the bug left over from the PY2-PY3 transition.
2151            while numdec / stride + 1 > numticks:
2152                stride += 1
2153        else:
2154            while numdec // stride + 1 > numticks:
2155                stride += 1
2156
2157        # Does subs include anything other than 1?
2158        have_subs = len(subs) > 1 or (len(subs) == 1 and subs[0] != 1.0)
2159
2160        decades = np.arange(math.floor(vmin) - stride,
2161                            math.ceil(vmax) + 2 * stride, stride)
2162
2163        if hasattr(self, '_transform'):
2164            ticklocs = self._transform.inverted().transform(decades)
2165            if have_subs:
2166                if stride == 1:
2167                    ticklocs = np.ravel(np.outer(subs, ticklocs))
2168                else:
2169                    ticklocs = []
2170        else:
2171            if have_subs:
2172                ticklocs = []
2173                if stride == 1:
2174                    for decadeStart in b ** decades:
2175                        ticklocs.extend(subs * decadeStart)
2176            else:
2177                ticklocs = b ** decades
2178
2179        return self.raise_if_exceeds(np.asarray(ticklocs))
2180
2181    def view_limits(self, vmin, vmax):
2182        'Try to choose the view limits intelligently'
2183        b = self._base
2184
2185        vmin, vmax = self.nonsingular(vmin, vmax)
2186
2187        if self.axis.axes.name == 'polar':
2188            vmax = math.ceil(math.log(vmax) / math.log(b))
2189            vmin = b ** (vmax - self.numdecs)
2190
2191        if rcParams['axes.autolimit_mode'] == 'round_numbers':
2192            if not is_decade(vmin, self._base):
2193                vmin = decade_down(vmin, self._base)
2194            if not is_decade(vmax, self._base):
2195                vmax = decade_up(vmax, self._base)
2196
2197        return vmin, vmax
2198
2199    def nonsingular(self, vmin, vmax):
2200        if not np.isfinite(vmin) or not np.isfinite(vmax):
2201            return 1, 10  # initial range, no data plotted yet
2202
2203        if vmin > vmax:
2204            vmin, vmax = vmax, vmin
2205        if vmax <= 0:
2206            warnings.warn(
2207                "Data has no positive values, and therefore cannot be "
2208                "log-scaled.")
2209            return 1, 10
2210
2211        minpos = self.axis.get_minpos()
2212        if not np.isfinite(minpos):
2213            minpos = 1e-300  # This should never take effect.
2214        if vmin <= 0:
2215            vmin = minpos
2216        if vmin == vmax:
2217            vmin = decade_down(vmin, self._base)
2218            vmax = decade_up(vmax, self._base)
2219        return vmin, vmax
2220
2221
2222class SymmetricalLogLocator(Locator):
2223    """
2224    Determine the tick locations for symmetric log axes
2225    """
2226
2227    def __init__(self, transform=None, subs=None, linthresh=None, base=None):
2228        """
2229        place ticks on the location= base**i*subs[j]
2230        """
2231        if transform is not None:
2232            self._base = transform.base
2233            self._linthresh = transform.linthresh
2234        elif linthresh is not None and base is not None:
2235            self._base = base
2236            self._linthresh = linthresh
2237        else:
2238            raise ValueError("Either transform, or both linthresh "
2239                             "and base, must be provided.")
2240        if subs is None:
2241            self._subs = [1.0]
2242        else:
2243            self._subs = subs
2244        self.numticks = 15
2245
2246    def set_params(self, subs=None, numticks=None):
2247        """Set parameters within this locator."""
2248        if numticks is not None:
2249            self.numticks = numticks
2250        if subs is not None:
2251            self._subs = subs
2252
2253    def __call__(self):
2254        'Return the locations of the ticks'
2255        # Note, these are untransformed coordinates
2256        vmin, vmax = self.axis.get_view_interval()
2257        return self.tick_values(vmin, vmax)
2258
2259    def tick_values(self, vmin, vmax):
2260        b = self._base
2261        t = self._linthresh
2262
2263        if vmax < vmin:
2264            vmin, vmax = vmax, vmin
2265
2266        # The domain is divided into three sections, only some of
2267        # which may actually be present.
2268        #
2269        # <======== -t ==0== t ========>
2270        # aaaaaaaaa    bbbbb   ccccccccc
2271        #
2272        # a) and c) will have ticks at integral log positions.  The
2273        # number of ticks needs to be reduced if there are more
2274        # than self.numticks of them.
2275        #
2276        # b) has a tick at 0 and only 0 (we assume t is a small
2277        # number, and the linear segment is just an implementation
2278        # detail and not interesting.)
2279        #
2280        # We could also add ticks at t, but that seems to usually be
2281        # uninteresting.
2282        #
2283        # "simple" mode is when the range falls entirely within (-t,
2284        # t) -- it should just display (vmin, 0, vmax)
2285
2286        has_a = has_b = has_c = False
2287        if vmin < -t:
2288            has_a = True
2289            if vmax > -t:
2290                has_b = True
2291                if vmax > t:
2292                    has_c = True
2293        elif vmin < 0:
2294            if vmax > 0:
2295                has_b = True
2296                if vmax > t:
2297                    has_c = True
2298            else:
2299                return [vmin, vmax]
2300        elif vmin < t:
2301            if vmax > t:
2302                has_b = True
2303                has_c = True
2304            else:
2305                return [vmin, vmax]
2306        else:
2307            has_c = True
2308
2309        def get_log_range(lo, hi):
2310            lo = np.floor(np.log(lo) / np.log(b))
2311            hi = np.ceil(np.log(hi) / np.log(b))
2312            return lo, hi
2313
2314        # First, calculate all the ranges, so we can determine striding
2315        if has_a:
2316            if has_b:
2317                a_range = get_log_range(t, -vmin + 1)
2318            else:
2319                a_range = get_log_range(-vmax, -vmin + 1)
2320        else:
2321            a_range = (0, 0)
2322
2323        if has_c:
2324            if has_b:
2325                c_range = get_log_range(t, vmax + 1)
2326            else:
2327                c_range = get_log_range(vmin, vmax + 1)
2328        else:
2329            c_range = (0, 0)
2330
2331        total_ticks = (a_range[1] - a_range[0]) + (c_range[1] - c_range[0])
2332        if has_b:
2333            total_ticks += 1
2334        stride = max(total_ticks // (self.numticks - 1), 1)
2335
2336        decades = []
2337        if has_a:
2338            decades.extend(-1 * (b ** (np.arange(a_range[0], a_range[1],
2339                                                 stride)[::-1])))
2340
2341        if has_b:
2342            decades.append(0.0)
2343
2344        if has_c:
2345            decades.extend(b ** (np.arange(c_range[0], c_range[1], stride)))
2346
2347        # Add the subticks if requested
2348        if self._subs is None:
2349            subs = np.arange(2.0, b)
2350        else:
2351            subs = np.asarray(self._subs)
2352
2353        if len(subs) > 1 or subs[0] != 1.0:
2354            ticklocs = []
2355            for decade in decades:
2356                if decade == 0:
2357                    ticklocs.append(decade)
2358                else:
2359                    ticklocs.extend(subs * decade)
2360        else:
2361            ticklocs = decades
2362
2363        return self.raise_if_exceeds(np.array(ticklocs))
2364
2365    def view_limits(self, vmin, vmax):
2366        'Try to choose the view limits intelligently'
2367        b = self._base
2368        if vmax < vmin:
2369            vmin, vmax = vmax, vmin
2370
2371        if rcParams['axes.autolimit_mode'] == 'round_numbers':
2372            if not is_decade(abs(vmin), b):
2373                if vmin < 0:
2374                    vmin = -decade_up(-vmin, b)
2375                else:
2376                    vmin = decade_down(vmin, b)
2377            if not is_decade(abs(vmax), b):
2378                if vmax < 0:
2379                    vmax = -decade_down(-vmax, b)
2380                else:
2381                    vmax = decade_up(vmax, b)
2382
2383            if vmin == vmax:
2384                if vmin < 0:
2385                    vmin = -decade_up(-vmin, b)
2386                    vmax = -decade_down(-vmax, b)
2387                else:
2388                    vmin = decade_down(vmin, b)
2389                    vmax = decade_up(vmax, b)
2390
2391        result = mtransforms.nonsingular(vmin, vmax)
2392        return result
2393
2394
2395class LogitLocator(Locator):
2396    """
2397    Determine the tick locations for logit axes
2398    """
2399
2400    def __init__(self, minor=False):
2401        """
2402        place ticks on the logit locations
2403        """
2404        self.minor = minor
2405
2406    def set_params(self, minor=None):
2407        """Set parameters within this locator."""
2408        if minor is not None:
2409            self.minor = minor
2410
2411    def __call__(self):
2412        'Return the locations of the ticks'
2413        vmin, vmax = self.axis.get_view_interval()
2414        return self.tick_values(vmin, vmax)
2415
2416    def tick_values(self, vmin, vmax):
2417        # dummy axis has no axes attribute
2418        if hasattr(self.axis, 'axes') and self.axis.axes.name == 'polar':
2419            raise NotImplementedError('Polar axis cannot be logit scaled yet')
2420
2421        vmin, vmax = self.nonsingular(vmin, vmax)
2422        vmin = np.log10(vmin / (1 - vmin))
2423        vmax = np.log10(vmax / (1 - vmax))
2424
2425        decade_min = np.floor(vmin)
2426        decade_max = np.ceil(vmax)
2427
2428        # major ticks
2429        if not self.minor:
2430            ticklocs = []
2431            if (decade_min <= -1):
2432                expo = np.arange(decade_min, min(0, decade_max + 1))
2433                ticklocs.extend(list(10**expo))
2434            if (decade_min <= 0) and (decade_max >= 0):
2435                ticklocs.append(0.5)
2436            if (decade_max >= 1):
2437                expo = -np.arange(max(1, decade_min), decade_max + 1)
2438                ticklocs.extend(list(1 - 10**expo))
2439
2440        # minor ticks
2441        else:
2442            ticklocs = []
2443            if (decade_min <= -2):
2444                expo = np.arange(decade_min, min(-1, decade_max))
2445                newticks = np.outer(np.arange(2, 10), 10**expo).ravel()
2446                ticklocs.extend(list(newticks))
2447            if (decade_min <= 0) and (decade_max >= 0):
2448                ticklocs.extend([0.2, 0.3, 0.4, 0.6, 0.7, 0.8])
2449            if (decade_max >= 2):
2450                expo = -np.arange(max(2, decade_min), decade_max + 1)
2451                newticks = 1 - np.outer(np.arange(2, 10), 10**expo).ravel()
2452                ticklocs.extend(list(newticks))
2453
2454        return self.raise_if_exceeds(np.array(ticklocs))
2455
2456    def nonsingular(self, vmin, vmax):
2457        initial_range = (1e-7, 1 - 1e-7)
2458        if not np.isfinite(vmin) or not np.isfinite(vmax):
2459            return initial_range  # no data plotted yet
2460
2461        if vmin > vmax:
2462            vmin, vmax = vmax, vmin
2463
2464        # what to do if a window beyond ]0, 1[ is chosen
2465        if self.axis is not None:
2466            minpos = self.axis.get_minpos()
2467            if not np.isfinite(minpos):
2468                return initial_range  # again, no data plotted
2469        else:
2470            minpos = 1e-7  # should not occur in normal use
2471
2472        # NOTE: for vmax, we should query a property similar to get_minpos, but
2473        # related to the maximal, less-than-one data point. Unfortunately,
2474        # Bbox._minpos is defined very deep in the BBox and updated with data,
2475        # so for now we use 1 - minpos as a substitute.
2476
2477        if vmin <= 0:
2478            vmin = minpos
2479        if vmax >= 1:
2480            vmax = 1 - minpos
2481        if vmin == vmax:
2482            return 0.1 * vmin, 1 - 0.1 * vmin
2483
2484        return vmin, vmax
2485
2486
2487class AutoLocator(MaxNLocator):
2488    """
2489    Dynamically find major tick positions. This is actually a subclass
2490    of `~matplotlib.ticker.MaxNLocator`, with parameters *nbins = 'auto'*
2491    and *steps = [1, 2, 2.5, 5, 10]*.
2492    """
2493    def __init__(self):
2494        """
2495        To know the values of the non-public parameters, please have a
2496        look to the defaults of `~matplotlib.ticker.MaxNLocator`.
2497        """
2498        if rcParams['_internal.classic_mode']:
2499            nbins = 9
2500            steps = [1, 2, 5, 10]
2501        else:
2502            nbins = 'auto'
2503            steps = [1, 2, 2.5, 5, 10]
2504        MaxNLocator.__init__(self, nbins=nbins, steps=steps)
2505
2506
2507class AutoMinorLocator(Locator):
2508    """
2509    Dynamically find minor tick positions based on the positions of
2510    major ticks. The scale must be linear with major ticks evenly spaced.
2511    """
2512    def __init__(self, n=None):
2513        """
2514        *n* is the number of subdivisions of the interval between
2515        major ticks; e.g., n=2 will place a single minor tick midway
2516        between major ticks.
2517
2518        If *n* is omitted or None, it will be set to 5 or 4.
2519        """
2520        self.ndivs = n
2521
2522    def __call__(self):
2523        'Return the locations of the ticks'
2524        if self.axis.get_scale() == 'log':
2525            warnings.warn('AutoMinorLocator does not work with logarithmic '
2526                          'scale')
2527            return []
2528
2529        majorlocs = self.axis.get_majorticklocs()
2530        try:
2531            majorstep = majorlocs[1] - majorlocs[0]
2532        except IndexError:
2533            # Need at least two major ticks to find minor tick locations
2534            # TODO: Figure out a way to still be able to display minor
2535            # ticks without two major ticks visible. For now, just display
2536            # no ticks at all.
2537            return []
2538
2539        if self.ndivs is None:
2540            x = int(np.round(10 ** (np.log10(majorstep) % 1)))
2541            if x in [1, 5, 10]:
2542                ndivs = 5
2543            else:
2544                ndivs = 4
2545        else:
2546            ndivs = self.ndivs
2547
2548        minorstep = majorstep / ndivs
2549
2550        vmin, vmax = self.axis.get_view_interval()
2551        if vmin > vmax:
2552            vmin, vmax = vmax, vmin
2553
2554        t0 = majorlocs[0]
2555        tmin = ((vmin - t0) // minorstep + 1) * minorstep
2556        tmax = ((vmax - t0) // minorstep + 1) * minorstep
2557        locs = np.arange(tmin, tmax, minorstep) + t0
2558        mod = np.abs((locs - t0) % majorstep)
2559        cond1 = mod > minorstep / 10.0
2560        cond2 = ~np.isclose(mod, majorstep, atol=0)
2561        locs = locs.compress(cond1 & cond2)
2562
2563        return self.raise_if_exceeds(np.array(locs))
2564
2565    def tick_values(self, vmin, vmax):
2566        raise NotImplementedError('Cannot get tick locations for a '
2567                                  '%s type.' % type(self))
2568
2569
2570class OldAutoLocator(Locator):
2571    """
2572    On autoscale this class picks the best MultipleLocator to set the
2573    view limits and the tick locs.
2574
2575    """
2576    def __init__(self):
2577        self._locator = LinearLocator()
2578
2579    def __call__(self):
2580        'Return the locations of the ticks'
2581        self.refresh()
2582        return self.raise_if_exceeds(self._locator())
2583
2584    def tick_values(self, vmin, vmax):
2585        raise NotImplementedError('Cannot get tick locations for a '
2586                                  '%s type.' % type(self))
2587
2588    def refresh(self):
2589        'refresh internal information based on current lim'
2590        vmin, vmax = self.axis.get_view_interval()
2591        vmin, vmax = mtransforms.nonsingular(vmin, vmax, expander=0.05)
2592        d = abs(vmax - vmin)
2593        self._locator = self.get_locator(d)
2594
2595    def view_limits(self, vmin, vmax):
2596        'Try to choose the view limits intelligently'
2597
2598        d = abs(vmax - vmin)
2599        self._locator = self.get_locator(d)
2600        return self._locator.view_limits(vmin, vmax)
2601
2602    def get_locator(self, d):
2603        'pick the best locator based on a distance'
2604        d = abs(d)
2605        if d <= 0:
2606            locator = MultipleLocator(0.2)
2607        else:
2608
2609            try:
2610                ld = math.log10(d)
2611            except OverflowError:
2612                raise RuntimeError('AutoLocator illegal data interval range')
2613
2614            fld = math.floor(ld)
2615            base = 10 ** fld
2616
2617            #if ld==fld:  base = 10**(fld-1)
2618            #else:        base = 10**fld
2619
2620            if d >= 5 * base:
2621                ticksize = base
2622            elif d >= 2 * base:
2623                ticksize = base / 2.0
2624            else:
2625                ticksize = base / 5.0
2626            locator = MultipleLocator(ticksize)
2627
2628        return locator
2629