''' Models for describing different kinds of ranges of values
in different kinds of spaces (e.g., continuous or categorical)
and with options for "auto sizing".
'''
from __future__ import absolute_import
from ..core.enums import PaddingUnits, StartEnd
from ..core.has_props import abstract
from ..core.properties import (Auto, Bool, Datetime, Either, Enum, Float, Instance, Int,
List, MinMaxBounds, String, TimeDelta)
from ..model import Model
from .callbacks import Callback
from .renderers import Renderer
@abstract
[docs]class Range(Model):
''' A base class for all range types.
'''
callback = Instance(Callback, help="""
A callback to run in the browser whenever the range is updated.
""")
[docs]class Range1d(Range):
''' A fixed, closed range [start, end] in a continuous scalar
dimension.
In addition to supplying ``start`` and ``end`` keyword arguments
to the ``Range1d`` initializer, you can also instantiate with
the convenience syntax::
Range(0, 10) # equivalent to Range(start=0, end=10)
'''
start = Either(Float, Datetime, Int, default=0, help="""
The start of the range.
""")
end = Either(Float, Datetime, Int, default=1, help="""
The end of the range.
""")
bounds = MinMaxBounds(accept_datetime=True, default=None, help="""
The bounds that the range is allowed to go to - typically used to prevent
the user from panning/zooming/etc away from the data.
If set to ``'auto'``, the bounds will be computed to the start and end of the Range.
Bounds are provided as a tuple of ``(min, max)`` so regardless of whether your range is
increasing or decreasing, the first item should be the minimum value of the range and the
second item should be the maximum. Setting min > max will result in a ``ValueError``.
By default, bounds are ``None`` and your plot to pan/zoom as far as you want. If you only
want to constrain one end of the plot, you can set min or max to None.
Examples:
Range1d(0, 1, bounds='auto') # Auto-bounded to 0 and 1 (Default behavior)
Range1d(start=0, end=1, bounds=(0, None)) # Maximum is unbounded, minimum bounded to 0
""")
min_interval = Either(Float, TimeDelta, Int, default=None, help="""
The level that the range is allowed to zoom in, expressed as the
minimum visible interval. If set to ``None`` (default), the minimum
interval is not bound. Can be a timedelta. """)
max_interval = Either(Float, TimeDelta, Int, default=None, help="""
The level that the range is allowed to zoom out, expressed as the
maximum visible interval. Can be a timedelta. Note that ``bounds`` can
impose an implicit constraint on the maximum interval as well. """)
def __init__(self, *args, **kwargs):
if args and ('start' in kwargs or 'end' in kwargs):
raise ValueError("'start' and 'end' keywords cannot be used with positional arguments")
if args and len(args) != 2:
raise ValueError('Only Range1d(start, end) acceptable when using positional arguments')
if args:
kwargs['start'] = args[0]
kwargs['end'] = args[1]
super(Range1d, self).__init__(**kwargs)
@abstract
[docs]class DataRange(Range):
''' A base class for all data range types.
'''
names = List(String, help="""
A list of names to query for. If set, only renderers that
have a matching value for their ``name`` attribute will be used
for autoranging.
""")
renderers = List(Instance(Renderer), help="""
An explicit list of renderers to autorange against. If unset,
defaults to all renderers on a plot.
""")
[docs]class DataRange1d(DataRange):
''' An auto-fitting range in a continuous scalar dimension.
The upper and lower bounds are set to the min and max of the data.
'''
range_padding = Float(default=0.1, help="""
How much padding to add around the computed data bounds.
When ``range_padding_units`` is set to ``"percent"``, the span of the
range span is expanded to make the range ``range_padding`` percent larger.
When ``range_padding_units`` is set to ``"absolute"``, the start and end
of the range span are extended by the amount ``range_padding``.
""")
range_padding_units = Enum(PaddingUnits, default="percent", help="""
Whether the ``range_padding`` should be interpreted as a percentage, or
as an absolute quantity. (default: ``"percent"``)
""")
start = Float(help="""
An explicitly supplied range start. If provided, will override
automatically computed start value.
""")
end = Float(help="""
An explicitly supplied range end. If provided, will override
automatically computed end value.
""")
bounds = MinMaxBounds(accept_datetime=False, default=None, help="""
The bounds that the range is allowed to go to - typically used to prevent
the user from panning/zooming/etc away from the data.
By default, the bounds will be None, allowing your plot to pan/zoom as far
as you want. If bounds are 'auto' they will be computed to be the same as
the start and end of the DataRange1d.
Bounds are provided as a tuple of ``(min, max)`` so regardless of whether
your range is increasing or decreasing, the first item should be the
minimum value of the range and the second item should be the maximum.
Setting ``min > max`` will result in a ``ValueError``.
If you only want to constrain one end of the plot, you can set ``min`` or
``max`` to ``None`` e.g. ``DataRange1d(bounds=(None, 12))``
""")
min_interval = Float(default=None, help="""
The level that the range is allowed to zoom in, expressed as the
minimum visible interval. If set to ``None`` (default), the minimum
interval is not bound.""")
max_interval = Float(default=None, help="""
The level that the range is allowed to zoom out, expressed as the
maximum visible interval. Note that ``bounds`` can impose an
implicit constraint on the maximum interval as well.""")
flipped = Bool(default=False, help="""
Whether the range should be "flipped" from its normal direction when
auto-ranging.
""")
follow = Enum(StartEnd, default=None, help="""
Configure the data to follow one or the other data extreme, with a
maximum range size of ``follow_interval``.
If set to ``"start"`` then the range will adjust so that ``start`` always
corresponds to the minimum data value (or maximum, if ``flipped`` is
``True``).
If set to ``"end"`` then the range will adjust so that ``end`` always
corresponds to the maximum data value (or minimum, if ``flipped`` is
``True``).
If set to ``None`` (default), then auto-ranging does not follow, and
the range will encompass both the minimum and maximum data values.
``follow`` cannot be used with bounds, and if set, bounds will be set to
``None``.
""")
follow_interval = Float(default=None, help="""
If ``follow`` is set to ``"start"`` or ``"end"`` then the range will
always be constrained to that::
abs(r.start - r.end) <= follow_interval
is maintained.
""")
default_span = Float(default=2.0, help="""
A default width for the interval, in case ``start`` is equal to ``end``
(if used with a log axis, default_span is in powers of 10).
""")
def __init__(self, *args, **kwargs):
if kwargs.get('follow') is not None:
kwargs['bounds'] = None
super(DataRange1d, self).__init__(**kwargs)
[docs]class FactorRange(Range):
''' A range in a categorical dimension.
In addition to supplying ``factors`` keyword argument to the
``FactorRange`` initializer, you can also instantiate with
the convenience syntax::
FactorRange("foo", "bar") # equivalent to FactorRange(factors=["foo", "bar"])
.. note::
``FactorRange`` may be renamed to ``CategoricalRange`` in
the future.
'''
offset = Float(0, help="""
An offset to the (synthetic) range (default: 0)
.. note::
The primary usage of this is to support compatibility and integration
with other plotting systems, and will not generally of interest to
most users.
""")
factors = Either(List(String), List(Int), help="""
A list of string or integer factors (categories) to comprise
this categorical range.
""")
bounds = Either(Auto, List(String), List(Int), default=None, help="""
The bounds that the range is allowed to go to - typically used to prevent
the user from panning/zooming/etc away from the data.
Unlike Range1d and DataRange1d, factors do not have an order and so a
min and max cannot be proved in the same way. bounds accepts a list of
factors, that constrain the displayed factors.
By default, bounds are ``None``, allows unlimited panning or zooming.
If ``bounds='auto'``, bounds will be the same as factors and the plot
will not be able to pan or zoom beyond the first and last factors.
If you provide a list, then only the factors that are in that list will
be displayed on the plot and the plot will not pan or zoom outside the
first and last items in the shortened factors list. Note the order of
factors is the defining order for your plot.
Values of bounds that are not in factors are acceptable and will simply
have no impact on the plot.
Examples:
Auto behavior:
.. code-block:: python
x_range = FactorRange(
factors=["apples", "dogs", "peaches", "bananas", "pigs"],
bounds='auto'
)
The plot will display all the factors and you will not be able to
pan left of apples or right of pigs.
Constraining behavior:
.. code-block:: python
x_range = FactorRange(
factors=["apples", "dogs", "peaches", "bananas", "pigs"],
bounds=["apples", "bananas", "peaches"]
)
Only the factors ``["apples", "peaches", "bananas"]`` (in that
order) will appear in the plot, and the plot will not pan left of
``"apples"`` or right of ``"bananas"``.
""")
min_interval = Int(default=None, help="""
The level that the range is allowed to zoom in, expressed as the
minimum number of visible categories. If set to ``None`` (default),
the minimum interval is not bound.""")
max_interval = Int(default=None, help="""
The level that the range is allowed to zoom out, expressed as the
maximum number of visible categories. Note that ``bounds`` can
impose an implicit constraint on the maximum interval as well.""")
def __init__(self, *args, **kwargs):
if args and "factors" in kwargs:
raise ValueError("'factors' keyword cannot be used with positional arguments")
elif args:
kwargs['factors'] = list(args)
super(FactorRange, self).__init__(**kwargs)