demo + utils venv
This commit is contained in:
602
utils/venv/lib/python3.6/site-packages/matplotlib/scale.py
Normal file
602
utils/venv/lib/python3.6/site-packages/matplotlib/scale.py
Normal file
@@ -0,0 +1,602 @@
|
||||
import numpy as np
|
||||
from numpy import ma
|
||||
|
||||
from matplotlib import cbook, docstring, rcParams
|
||||
from matplotlib.ticker import (
|
||||
NullFormatter, ScalarFormatter, LogFormatterSciNotation, LogitFormatter,
|
||||
NullLocator, LogLocator, AutoLocator, AutoMinorLocator,
|
||||
SymmetricalLogLocator, LogitLocator)
|
||||
from matplotlib.transforms import Transform, IdentityTransform
|
||||
|
||||
|
||||
class ScaleBase(object):
|
||||
"""
|
||||
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`
|
||||
"""
|
||||
def get_transform(self):
|
||||
"""
|
||||
Return the :class:`~matplotlib.transforms.Transform` object
|
||||
associated with this scale.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def set_default_locators_and_formatters(self, axis):
|
||||
"""
|
||||
Set the :class:`~matplotlib.ticker.Locator` and
|
||||
:class:`~matplotlib.ticker.Formatter` objects on the given
|
||||
axis to match this scale.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def limit_range_for_scale(self, vmin, vmax, minpos):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
return vmin, vmax
|
||||
|
||||
|
||||
class LinearScale(ScaleBase):
|
||||
"""
|
||||
The default linear scale.
|
||||
"""
|
||||
|
||||
name = 'linear'
|
||||
|
||||
def __init__(self, axis, **kwargs):
|
||||
pass
|
||||
|
||||
def set_default_locators_and_formatters(self, axis):
|
||||
"""
|
||||
Set the locators and formatters to reasonable defaults for
|
||||
linear scaling.
|
||||
"""
|
||||
axis.set_major_locator(AutoLocator())
|
||||
axis.set_major_formatter(ScalarFormatter())
|
||||
axis.set_minor_formatter(NullFormatter())
|
||||
# update the minor locator for x and y axis based on rcParams
|
||||
if rcParams['xtick.minor.visible']:
|
||||
axis.set_minor_locator(AutoMinorLocator())
|
||||
else:
|
||||
axis.set_minor_locator(NullLocator())
|
||||
|
||||
def get_transform(self):
|
||||
"""
|
||||
The transform for linear scaling is just the
|
||||
:class:`~matplotlib.transforms.IdentityTransform`.
|
||||
"""
|
||||
return IdentityTransform()
|
||||
|
||||
|
||||
class LogTransformBase(Transform):
|
||||
input_dims = 1
|
||||
output_dims = 1
|
||||
is_separable = True
|
||||
has_inverse = True
|
||||
|
||||
def __init__(self, nonpos='clip'):
|
||||
Transform.__init__(self)
|
||||
self._clip = {"clip": True, "mask": False}[nonpos]
|
||||
|
||||
def transform_non_affine(self, a):
|
||||
# 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")
|
||||
|
||||
|
||||
class InvertedLogTransformBase(Transform):
|
||||
input_dims = 1
|
||||
output_dims = 1
|
||||
is_separable = True
|
||||
has_inverse = True
|
||||
|
||||
def transform_non_affine(self, a):
|
||||
return ma.power(self.base, a)
|
||||
|
||||
def __str__(self):
|
||||
return "{}()".format(type(self).__name__)
|
||||
|
||||
|
||||
class Log10Transform(LogTransformBase):
|
||||
base = 10.0
|
||||
|
||||
def inverted(self):
|
||||
return InvertedLog10Transform()
|
||||
|
||||
|
||||
class InvertedLog10Transform(InvertedLogTransformBase):
|
||||
base = 10.0
|
||||
|
||||
def inverted(self):
|
||||
return Log10Transform()
|
||||
|
||||
|
||||
class Log2Transform(LogTransformBase):
|
||||
base = 2.0
|
||||
|
||||
def inverted(self):
|
||||
return InvertedLog2Transform()
|
||||
|
||||
|
||||
class InvertedLog2Transform(InvertedLogTransformBase):
|
||||
base = 2.0
|
||||
|
||||
def inverted(self):
|
||||
return Log2Transform()
|
||||
|
||||
|
||||
class NaturalLogTransform(LogTransformBase):
|
||||
base = np.e
|
||||
|
||||
def inverted(self):
|
||||
return InvertedNaturalLogTransform()
|
||||
|
||||
|
||||
class InvertedNaturalLogTransform(InvertedLogTransformBase):
|
||||
base = np.e
|
||||
|
||||
def inverted(self):
|
||||
return NaturalLogTransform()
|
||||
|
||||
|
||||
class LogTransform(LogTransformBase):
|
||||
def __init__(self, base, nonpos='clip'):
|
||||
LogTransformBase.__init__(self, nonpos)
|
||||
self.base = base
|
||||
|
||||
def inverted(self):
|
||||
return InvertedLogTransform(self.base)
|
||||
|
||||
|
||||
class InvertedLogTransform(InvertedLogTransformBase):
|
||||
def __init__(self, base):
|
||||
InvertedLogTransformBase.__init__(self)
|
||||
self.base = base
|
||||
|
||||
def inverted(self):
|
||||
return LogTransform(self.base)
|
||||
|
||||
|
||||
class LogScale(ScaleBase):
|
||||
"""
|
||||
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`)
|
||||
"""
|
||||
name = 'log'
|
||||
|
||||
# compatibility shim
|
||||
LogTransformBase = LogTransformBase
|
||||
Log10Transform = Log10Transform
|
||||
InvertedLog10Transform = InvertedLog10Transform
|
||||
Log2Transform = Log2Transform
|
||||
InvertedLog2Transform = InvertedLog2Transform
|
||||
NaturalLogTransform = NaturalLogTransform
|
||||
InvertedNaturalLogTransform = InvertedNaturalLogTransform
|
||||
LogTransform = LogTransform
|
||||
InvertedLogTransform = InvertedLogTransform
|
||||
|
||||
def __init__(self, axis, **kwargs):
|
||||
"""
|
||||
*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
|
||||
|
||||
def set_default_locators_and_formatters(self, axis):
|
||||
"""
|
||||
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)))
|
||||
|
||||
def get_transform(self):
|
||||
"""
|
||||
Return a :class:`~matplotlib.transforms.Transform` instance
|
||||
appropriate for the given logarithm base.
|
||||
"""
|
||||
return self._transform
|
||||
|
||||
def limit_range_for_scale(self, vmin, vmax, minpos):
|
||||
"""
|
||||
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)
|
||||
|
||||
|
||||
class SymmetricalLogTransform(Transform):
|
||||
input_dims = 1
|
||||
output_dims = 1
|
||||
is_separable = True
|
||||
has_inverse = True
|
||||
|
||||
def __init__(self, base, linthresh, linscale):
|
||||
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)
|
||||
|
||||
def transform_non_affine(self, a):
|
||||
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
|
||||
|
||||
def inverted(self):
|
||||
return InvertedSymmetricalLogTransform(self.base, self.linthresh,
|
||||
self.linscale)
|
||||
|
||||
|
||||
class InvertedSymmetricalLogTransform(Transform):
|
||||
input_dims = 1
|
||||
output_dims = 1
|
||||
is_separable = True
|
||||
has_inverse = True
|
||||
|
||||
def __init__(self, base, linthresh, 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))
|
||||
|
||||
def transform_non_affine(self, a):
|
||||
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
|
||||
|
||||
def inverted(self):
|
||||
return SymmetricalLogTransform(self.base,
|
||||
self.linthresh, self.linscale)
|
||||
|
||||
|
||||
class SymmetricalLogScale(ScaleBase):
|
||||
"""
|
||||
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*).
|
||||
"""
|
||||
name = 'symlog'
|
||||
# compatibility shim
|
||||
SymmetricalLogTransform = SymmetricalLogTransform
|
||||
InvertedSymmetricalLogTransform = InvertedSymmetricalLogTransform
|
||||
|
||||
def __init__(self, axis, **kwargs):
|
||||
"""
|
||||
*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
|
||||
|
||||
def set_default_locators_and_formatters(self, axis):
|
||||
"""
|
||||
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())
|
||||
|
||||
def get_transform(self):
|
||||
"""
|
||||
Return a :class:`SymmetricalLogTransform` instance.
|
||||
"""
|
||||
return self._transform
|
||||
|
||||
|
||||
class LogitTransform(Transform):
|
||||
input_dims = 1
|
||||
output_dims = 1
|
||||
is_separable = True
|
||||
has_inverse = True
|
||||
|
||||
def __init__(self, nonpos='mask'):
|
||||
Transform.__init__(self)
|
||||
self._nonpos = nonpos
|
||||
self._clip = {"clip": True, "mask": False}[nonpos]
|
||||
|
||||
def transform_non_affine(self, a):
|
||||
"""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
|
||||
|
||||
def inverted(self):
|
||||
return LogisticTransform(self._nonpos)
|
||||
|
||||
def __str__(self):
|
||||
return "{}({!r})".format(type(self).__name__,
|
||||
"clip" if self._clip else "mask")
|
||||
|
||||
|
||||
class LogisticTransform(Transform):
|
||||
input_dims = 1
|
||||
output_dims = 1
|
||||
is_separable = True
|
||||
has_inverse = True
|
||||
|
||||
def __init__(self, nonpos='mask'):
|
||||
Transform.__init__(self)
|
||||
self._nonpos = nonpos
|
||||
|
||||
def transform_non_affine(self, a):
|
||||
"""logistic transform (base 10)"""
|
||||
return 1.0 / (1 + 10**(-a))
|
||||
|
||||
def inverted(self):
|
||||
return LogitTransform(self._nonpos)
|
||||
|
||||
def __str__(self):
|
||||
return "{}({!r})".format(type(self).__name__, self._nonpos)
|
||||
|
||||
|
||||
class LogitScale(ScaleBase):
|
||||
"""
|
||||
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[.
|
||||
"""
|
||||
name = 'logit'
|
||||
|
||||
def __init__(self, axis, nonpos='mask'):
|
||||
"""
|
||||
*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)
|
||||
|
||||
def get_transform(self):
|
||||
"""
|
||||
Return a :class:`LogitTransform` instance.
|
||||
"""
|
||||
return self._transform
|
||||
|
||||
def set_default_locators_and_formatters(self, axis):
|
||||
# ..., 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())
|
||||
|
||||
def limit_range_for_scale(self, vmin, vmax, minpos):
|
||||
"""
|
||||
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)
|
||||
|
||||
|
||||
_scale_mapping = {
|
||||
'linear': LinearScale,
|
||||
'log': LogScale,
|
||||
'symlog': SymmetricalLogScale,
|
||||
'logit': LogitScale,
|
||||
}
|
||||
|
||||
|
||||
def get_scale_names():
|
||||
return sorted(_scale_mapping)
|
||||
|
||||
|
||||
def scale_factory(scale, axis, **kwargs):
|
||||
"""
|
||||
Return a scale class by name.
|
||||
|
||||
ACCEPTS: [ %(names)s ]
|
||||
"""
|
||||
scale = scale.lower()
|
||||
if scale is None:
|
||||
scale = 'linear'
|
||||
|
||||
if scale not in _scale_mapping:
|
||||
raise ValueError("Unknown scale type '%s'" % scale)
|
||||
|
||||
return _scale_mapping[scale](axis, **kwargs)
|
||||
scale_factory.__doc__ = cbook.dedent(scale_factory.__doc__) % \
|
||||
{'names': " | ".join(get_scale_names())}
|
||||
|
||||
|
||||
def register_scale(scale_class):
|
||||
"""
|
||||
Register a new kind of scale.
|
||||
|
||||
*scale_class* must be a subclass of :class:`ScaleBase`.
|
||||
"""
|
||||
_scale_mapping[scale_class.name] = scale_class
|
||||
|
||||
|
||||
def get_scale_docs():
|
||||
"""
|
||||
Helper function for generating docstrings related to scales.
|
||||
"""
|
||||
docs = []
|
||||
for name in get_scale_names():
|
||||
scale_class = _scale_mapping[name]
|
||||
docs.append(" '%s'" % name)
|
||||
docs.append("")
|
||||
class_docs = cbook.dedent(scale_class.__init__.__doc__)
|
||||
class_docs = "".join([" %s\n" %
|
||||
x for x in class_docs.split("\n")])
|
||||
docs.append(class_docs)
|
||||
docs.append("")
|
||||
return "\n".join(docs)
|
||||
|
||||
|
||||
docstring.interpd.update(
|
||||
scale=' | '.join([repr(x) for x in get_scale_names()]),
|
||||
scale_docs=get_scale_docs().rstrip(),
|
||||
)
|
||||
Reference in New Issue
Block a user