NullFormatter, ScalarFormatter, LogFormatterSciNotation, LogitFormatter, NullLocator, LogLocator, AutoLocator, AutoMinorLocator, SymmetricalLogLocator, LogitLocator)
""" The base class for all scales.
Scales are separable transformations, working on a single dimension.
Any subclasses will want to override:
- :attr:`name` - :meth:`get_transform` - :meth:`set_default_locators_and_formatters`
And optionally: - :meth:`limit_range_for_scale` """ """ Return the :class:`~matplotlib.transforms.Transform` object associated with this scale. """ raise NotImplementedError()
""" Set the :class:`~matplotlib.ticker.Locator` and :class:`~matplotlib.ticker.Formatter` objects on the given axis to match this scale. """ raise NotImplementedError()
""" Returns the range *vmin*, *vmax*, possibly limited to the domain supported by this scale.
*minpos* should be the minimum positive value in the data. This is used by log scales to determine a minimum value. """
""" The default linear scale. """
""" Set the locators and formatters to reasonable defaults for linear scaling. """ # update the minor locator for x and y axis based on rcParams axis.set_minor_locator(AutoMinorLocator()) else:
""" The transform for linear scaling is just the :class:`~matplotlib.transforms.IdentityTransform`. """
Transform.__init__(self) self._clip = {"clip": True, "mask": False}[nonpos]
# Ignore invalid values due to nans being passed to the transform with np.errstate(divide="ignore", invalid="ignore"): out = np.log(a) out /= np.log(self.base) if self._clip: # SVG spec says that conforming viewers must support values up # to 3.4e38 (C float); however experiments suggest that # Inkscape (which uses cairo for rendering) runs into cairo's # 24-bit limit (which is apparently shared by Agg). # Ghostscript (used for pdf rendering appears to overflow even # earlier, with the max value around 2 ** 15 for the tests to # pass. On the other hand, in practice, we want to clip beyond # np.log10(np.nextafter(0, 1)) ~ -323 # so 1000 seems safe. out[a <= 0] = -1000 return out
def __str__(self): return "{}({!r})".format( type(self).__name__, "clip" if self._clip else "mask")
return ma.power(self.base, a)
def __str__(self): return "{}()".format(type(self).__name__)
return InvertedLog10Transform()
return Log10Transform()
return InvertedLog2Transform()
return Log2Transform()
return InvertedNaturalLogTransform()
return NaturalLogTransform()
LogTransformBase.__init__(self, nonpos) self.base = base
return InvertedLogTransform(self.base)
InvertedLogTransformBase.__init__(self) self.base = base
return LogTransform(self.base)
""" A standard logarithmic scale. Care is taken so non-positive values are not plotted.
For computational efficiency (to push as much as possible to Numpy C code in the common cases), this scale provides different transforms depending on the base of the logarithm:
- base 10 (:class:`Log10Transform`) - base 2 (:class:`Log2Transform`) - base e (:class:`NaturalLogTransform`) - arbitrary base (:class:`LogTransform`) """
# compatibility shim
""" *basex*/*basey*: The base of the logarithm
*nonposx*/*nonposy*: {'mask', 'clip'} non-positive values in *x* or *y* can be masked as invalid, or clipped to a very small positive number
*subsx*/*subsy*: Where to place the subticks between each major tick. Should be a sequence of integers. For example, in a log10 scale: ``[2, 3, 4, 5, 6, 7, 8, 9]``
will place 8 logarithmically spaced minor ticks between each major tick. """ if axis.axis_name == 'x': base = kwargs.pop('basex', 10.0) subs = kwargs.pop('subsx', None) nonpos = kwargs.pop('nonposx', 'clip') else: base = kwargs.pop('basey', 10.0) subs = kwargs.pop('subsy', None) nonpos = kwargs.pop('nonposy', 'clip')
if len(kwargs): raise ValueError(("provided too many kwargs, can only pass " "{'basex', 'subsx', nonposx'} or " "{'basey', 'subsy', nonposy'}. You passed ") + "{!r}".format(kwargs))
if nonpos not in ['mask', 'clip']: raise ValueError("nonposx, nonposy kwarg must be 'mask' or 'clip'") if base <= 0 or base == 1: raise ValueError('The log base cannot be <= 0 or == 1')
if base == 10.0: self._transform = self.Log10Transform(nonpos) elif base == 2.0: self._transform = self.Log2Transform(nonpos) elif base == np.e: self._transform = self.NaturalLogTransform(nonpos) else: self._transform = self.LogTransform(base, nonpos)
self.base = base self.subs = subs
""" Set the locators and formatters to specialized versions for log scaling. """ axis.set_major_locator(LogLocator(self.base)) axis.set_major_formatter(LogFormatterSciNotation(self.base)) axis.set_minor_locator(LogLocator(self.base, self.subs)) axis.set_minor_formatter( LogFormatterSciNotation(self.base, labelOnlyBase=(self.subs is not None)))
""" Return a :class:`~matplotlib.transforms.Transform` instance appropriate for the given logarithm base. """ return self._transform
""" Limit the domain to positive values. """ if not np.isfinite(minpos): minpos = 1e-300 # This value should rarely if ever # end up with a visible effect.
return (minpos if vmin <= 0 else vmin, minpos if vmax <= 0 else vmax)
Transform.__init__(self) self.base = base self.linthresh = linthresh self.linscale = linscale self._linscale_adj = (linscale / (1.0 - self.base ** -1)) self._log_base = np.log(base)
sign = np.sign(a) masked = ma.masked_inside(a, -self.linthresh, self.linthresh, copy=False) log = sign * self.linthresh * ( self._linscale_adj + ma.log(np.abs(masked) / self.linthresh) / self._log_base) if masked.mask.any(): return ma.where(masked.mask, a * self._linscale_adj, log) else: return log
return InvertedSymmetricalLogTransform(self.base, self.linthresh, self.linscale)
Transform.__init__(self) symlog = SymmetricalLogTransform(base, linthresh, linscale) self.base = base self.linthresh = linthresh self.invlinthresh = symlog.transform(linthresh) self.linscale = linscale self._linscale_adj = (linscale / (1.0 - self.base ** -1))
sign = np.sign(a) masked = ma.masked_inside(a, -self.invlinthresh, self.invlinthresh, copy=False) exp = sign * self.linthresh * ( ma.power(self.base, (sign * (masked / self.linthresh)) - self._linscale_adj)) if masked.mask.any(): return ma.where(masked.mask, a / self._linscale_adj, exp) else: return exp
return SymmetricalLogTransform(self.base, self.linthresh, self.linscale)
""" The symmetrical logarithmic scale is logarithmic in both the positive and negative directions from the origin.
Since the values close to zero tend toward infinity, there is a need to have a range around zero that is linear. The parameter *linthresh* allows the user to specify the size of this range (-*linthresh*, *linthresh*). """ # compatibility shim
""" *basex*/*basey*: The base of the logarithm
*linthreshx*/*linthreshy*: A single float which defines the range (-*x*, *x*), within which the plot is linear. This avoids having the plot go to infinity around zero.
*subsx*/*subsy*: Where to place the subticks between each major tick. Should be a sequence of integers. For example, in a log10 scale: ``[2, 3, 4, 5, 6, 7, 8, 9]``
will place 8 logarithmically spaced minor ticks between each major tick.
*linscalex*/*linscaley*: This allows the linear range (-*linthresh* to *linthresh*) to be stretched relative to the logarithmic range. Its value is the number of decades to use for each half of the linear range. For example, when *linscale* == 1.0 (the default), the space used for the positive and negative halves of the linear range will be equal to one decade in the logarithmic range. """ if axis.axis_name == 'x': base = kwargs.pop('basex', 10.0) linthresh = kwargs.pop('linthreshx', 2.0) subs = kwargs.pop('subsx', None) linscale = kwargs.pop('linscalex', 1.0) else: base = kwargs.pop('basey', 10.0) linthresh = kwargs.pop('linthreshy', 2.0) subs = kwargs.pop('subsy', None) linscale = kwargs.pop('linscaley', 1.0)
if base <= 1.0: raise ValueError("'basex/basey' must be larger than 1") if linthresh <= 0.0: raise ValueError("'linthreshx/linthreshy' must be positive") if linscale <= 0.0: raise ValueError("'linscalex/linthreshy' must be positive")
self._transform = self.SymmetricalLogTransform(base, linthresh, linscale)
self.base = base self.linthresh = linthresh self.linscale = linscale self.subs = subs
""" Set the locators and formatters to specialized versions for symmetrical log scaling. """ axis.set_major_locator(SymmetricalLogLocator(self.get_transform())) axis.set_major_formatter(LogFormatterSciNotation(self.base)) axis.set_minor_locator(SymmetricalLogLocator(self.get_transform(), self.subs)) axis.set_minor_formatter(NullFormatter())
""" Return a :class:`SymmetricalLogTransform` instance. """ return self._transform
Transform.__init__(self) self._nonpos = nonpos self._clip = {"clip": True, "mask": False}[nonpos]
"""logit transform (base 10), masked or clipped""" with np.errstate(divide="ignore", invalid="ignore"): out = np.log10(a / (1 - a)) if self._clip: # See LogTransform for choice of clip value. out[a <= 0] = -1000 out[1 <= a] = 1000 return out
return LogisticTransform(self._nonpos)
def __str__(self): return "{}({!r})".format(type(self).__name__, "clip" if self._clip else "mask")
Transform.__init__(self) self._nonpos = nonpos
"""logistic transform (base 10)""" return 1.0 / (1 + 10**(-a))
return LogitTransform(self._nonpos)
def __str__(self): return "{}({!r})".format(type(self).__name__, self._nonpos)
""" Logit scale for data between zero and one, both excluded.
This scale is similar to a log scale close to zero and to one, and almost linear around 0.5. It maps the interval ]0, 1[ onto ]-infty, +infty[. """
""" *nonpos*: {'mask', 'clip'} values beyond ]0, 1[ can be masked as invalid, or clipped to a number very close to 0 or 1 """ if nonpos not in ['mask', 'clip']: raise ValueError("nonposx, nonposy kwarg must be 'mask' or 'clip'")
self._transform = LogitTransform(nonpos)
""" Return a :class:`LogitTransform` instance. """ return self._transform
# ..., 0.01, 0.1, 0.5, 0.9, 0.99, ... axis.set_major_locator(LogitLocator()) axis.set_major_formatter(LogitFormatter()) axis.set_minor_locator(LogitLocator(minor=True)) axis.set_minor_formatter(LogitFormatter())
""" Limit the domain to values between 0 and 1 (excluded). """ if not np.isfinite(minpos): minpos = 1e-7 # This value should rarely if ever # end up with a visible effect. return (minpos if vmin <= 0 else vmin, 1 - minpos if vmax >= 1 else vmax)
'linear': LinearScale, 'log': LogScale, 'symlog': SymmetricalLogScale, 'logit': LogitScale, }
""" Return a scale class by name.
ACCEPTS: [ %(names)s ] """ scale = 'linear'
raise ValueError("Unknown scale type '%s'" % scale)
{'names': " | ".join(get_scale_names())}
""" Register a new kind of scale.
*scale_class* must be a subclass of :class:`ScaleBase`. """ _scale_mapping[scale_class.name] = scale_class
""" Helper function for generating docstrings related to scales. """ x for x in class_docs.split("\n")])
scale=' | '.join([repr(x) for x in get_scale_names()]), scale_docs=get_scale_docs().rstrip(), ) |