<commit_before>"""
Supplies Layer and related classes that allow overlaying of Views,
including Overlay. A Layer is the final extension of View base class
that allows Views to be overlayed on top of each other.

Also supplies ViewMap which is the primary multi-dimensional Map type
for indexing, slicing and animating collections of Views.
"""

from collections import OrderedDict, defaultdict
import itertools
import numpy as np

import param

from .dimension import Dimension
from .ndmapping import NdMapping
from .layout import Pane, GridLayout, AdjointLayout
from .options import options, channels
from .view import View, Map


def find_minmax(lims, olims):
    """
    Takes (a1, a2) and (b1, b2) as input and returns
    (np.min(a1, b1), np.max(a2, b2)). Used to calculate
    min and max values of a number of items.
    """

    limzip = zip(list(lims), list(olims), [np.min, np.max])
    return tuple([float(fn([l, ol])) for l, ol, fn in limzip])


class Layer(Pane):
    """
    Layer is the baseclass for all 2D View types, with an x- and
    y-dimension. Subclasses should define the data storage in the
    constructor, as well as methods and properties, which define how
    the data maps onto the x- and y- and value dimensions.
    """

    dimensions = param.List(default=[Dimension('X')], doc="""
        Dimensions on Layers determine the number of indexable
        dimensions.""")

    value = param.ClassSelector(class_=Dimension, default=Dimension('Y'))


    def __mul__(self, other):
        if isinstance(other, ViewMap):
            items = [(k, self * v) for (k, v) in other.items()]
            return other.clone(items=items)

        self_layers = self.data if isinstance(self, Overlay) else [self]
        other_layers = other.data if isinstance(other, Overlay) else [other]
        combined_layers = self_layers + other_layers

        return Overlay(combined_layers)


    ########################
    # Subclassable methods #
    ########################


    def __init__(self, data, **kwargs):
        self._xlim = None
        self._ylim = None
        super(Layer, self).__init__(data, **kwargs)


    @property
    def cyclic_range(self):
        if self.dimensions[0].cyclic:
            return self.dimensions[0].range[1]
        else:
            return None

    @property
    def range(self):
        if self.cyclic_range:
            return self.cyclic_range
        y_vals = self.data[:, 1]
        return (float(min(y_vals)), float(max(y_vals)))

    @property
    def xlabel(self):
        return self.dimensions[0].pprint_label

    @property
    def ylabel(self):
        if len(self.dimensions) == 1:
            return self.value.pprint_label
        else:
            return self.dimensions[1].pprint_label

    @property
    def xlim(self):
        if self._xlim:
            return self._xlim
        elif self.cyclic_range is not None:
            return (0, self.cyclic_range)
        else:
            x_vals = self.data[:, 0]
            return (float(min(x_vals)), float(max(x_vals)))

    @xlim.setter
    def xlim(self, limits):
        if self.cyclic_range:
            self.warning('Cannot override the limits of a '
                         'cyclic dimension.')
        elif limits is None or (isinstance(limits, tuple) and len(limits) == 2):
            self._xlim = limits
        else:
            raise ValueError('xlim needs to be a length two tuple or None.')

    @property
    def ylim(self):
        if self._ylim:
            return self._ylim
        else:
            y_vals = self.data[:, 1]
            return (float(min(y_vals)), float(max(y_vals)))

    @ylim.setter
    def ylim(self, limits):
        if limits is None or (isinstance(limits, tuple) and len(limits) == 2):
            self._ylim = limits
        else:
            raise ValueError('xlim needs to be a length two tuple or None.')

    @property
    def lbrt(self):
        l, r = self.xlim if self.xlim else (np.NaN, np.NaN)
        b, t = self.ylim if self.ylim else (np.NaN, np.NaN)
        return l, b, r, t


    @lbrt.setter
    def lbrt(self, lbrt):
        l, b, r, t = lbrt
        self.xlim, self.ylim = (l, r), (b, t)



class Overlay(Pane, NdMapping):
    """
    An Overlay allows a group of Layers to be overlaid together. Layers can
    be indexed out of an overlay and an overlay is an iterable that iterates
    over the contained layers.

    A SheetOverlay may be used to overlay lines or points over a
    SheetMatrix. In addition, if an overlay consists of three or four
    SheetViews of depth 1, the overlay may be converted to an RGB(A)
    SheetMatrix via the rgb property.
    """

    dimensions = param.List(default=[Dimension('Layer')], constant=True, doc="""List
      of dimensions the Overlay can be indexed by.""")

    label = param.String(doc="""
      A short label used to indicate what kind of data is contained
      within the view object.

      Overlays should not have their label set directly by the user as
      the label is only for defining custom channel operations.""")

    title = param.String(default="{label}")

    channels = channels
    _deep_indexable = True

    def __init__(self, overlays, **kwargs):
        self._xlim = None
        self._ylim = None
        data = self._process_layers(overlays)
        super(Overlay, self).__init__(data, **kwargs)
        self._data = self.data


    def _process_layers(self, layers):
        """
        Set a collection of layers to be overlaid with each other.
        """
        if isinstance(layers, ViewMap):
            return layers
        keys = range(len(layers))
        data = OrderedDict(((key,), layer) for key, layer in zip(keys, layers))
        return data


    def set(self, layers):
        data = self._process_layers(layers)
        self._data = data


    @property
    def labels(self):
        return [el.label for el in self]


    @property
    def legend(self):
        if self.dimension_labels == ['Layer']:
            labels = self.labels
            if len(set(labels)) == len(labels):
                return labels
            else:
                return None
        else:
            labels = []
            for key in self.keys():
                labels.append(','.join([dim.pprint_value(k) for dim, k in
                                        zip(self.dimensions, key)]))
            return labels
<commit_msg>Enabled creation of empty Overlays<commit_after>"""
Supplies Layer and related classes that allow overlaying of Views,
including Overlay. A Layer is the final extension of View base class
that allows Views to be overlayed on top of each other.

Also supplies ViewMap which is the primary multi-dimensional Map type
for indexing, slicing and animating collections of Views.
"""

from collections import OrderedDict, defaultdict
import itertools
import numpy as np

import param

from .dimension import Dimension
from .ndmapping import NdMapping
from .layout import Pane, GridLayout, AdjointLayout
from .options import options, channels
from .view import View, Map


def find_minmax(lims, olims):
    """
    Takes (a1, a2) and (b1, b2) as input and returns
    (np.min(a1, b1), np.max(a2, b2)). Used to calculate
    min and max values of a number of items.
    """

    limzip = zip(list(lims), list(olims), [np.min, np.max])
    return tuple([float(fn([l, ol])) for l, ol, fn in limzip])


class Layer(Pane):
    """
    Layer is the baseclass for all 2D View types, with an x- and
    y-dimension. Subclasses should define the data storage in the
    constructor, as well as methods and properties, which define how
    the data maps onto the x- and y- and value dimensions.
    """

    dimensions = param.List(default=[Dimension('X')], doc="""
        Dimensions on Layers determine the number of indexable
        dimensions.""")

    value = param.ClassSelector(class_=Dimension, default=Dimension('Y'))


    def __mul__(self, other):
        if isinstance(other, ViewMap):
            items = [(k, self * v) for (k, v) in other.items()]
            return other.clone(items=items)

        self_layers = self.data if isinstance(self, Overlay) else [self]
        other_layers = other.data if isinstance(other, Overlay) else [other]
        combined_layers = self_layers + other_layers

        return Overlay(combined_layers)


    ########################
    # Subclassable methods #
    ########################


    def __init__(self, data, **kwargs):
        self._xlim = None
        self._ylim = None
        super(Layer, self).__init__(data, **kwargs)


    @property
    def cyclic_range(self):
        if self.dimensions[0].cyclic:
            return self.dimensions[0].range[1]
        else:
            return None

    @property
    def range(self):
        if self.cyclic_range:
            return self.cyclic_range
        y_vals = self.data[:, 1]
        return (float(min(y_vals)), float(max(y_vals)))

    @property
    def xlabel(self):
        return self.dimensions[0].pprint_label

    @property
    def ylabel(self):
        if len(self.dimensions) == 1:
            return self.value.pprint_label
        else:
            return self.dimensions[1].pprint_label

    @property
    def xlim(self):
        if self._xlim:
            return self._xlim
        elif self.cyclic_range is not None:
            return (0, self.cyclic_range)
        else:
            x_vals = self.data[:, 0]
            return (float(min(x_vals)), float(max(x_vals)))

    @xlim.setter
    def xlim(self, limits):
        if self.cyclic_range:
            self.warning('Cannot override the limits of a '
                         'cyclic dimension.')
        elif limits is None or (isinstance(limits, tuple) and len(limits) == 2):
            self._xlim = limits
        else:
            raise ValueError('xlim needs to be a length two tuple or None.')

    @property
    def ylim(self):
        if self._ylim:
            return self._ylim
        else:
            y_vals = self.data[:, 1]
            return (float(min(y_vals)), float(max(y_vals)))

    @ylim.setter
    def ylim(self, limits):
        if limits is None or (isinstance(limits, tuple) and len(limits) == 2):
            self._ylim = limits
        else:
            raise ValueError('xlim needs to be a length two tuple or None.')

    @property
    def lbrt(self):
        l, r = self.xlim if self.xlim else (np.NaN, np.NaN)
        b, t = self.ylim if self.ylim else (np.NaN, np.NaN)
        return l, b, r, t


    @lbrt.setter
    def lbrt(self, lbrt):
        l, b, r, t = lbrt
        self.xlim, self.ylim = (l, r), (b, t)



class Overlay(Pane, NdMapping):
    """
    An Overlay allows a group of Layers to be overlaid together. Layers can
    be indexed out of an overlay and an overlay is an iterable that iterates
    over the contained layers.

    A SheetOverlay may be used to overlay lines or points over a
    SheetMatrix. In addition, if an overlay consists of three or four
    SheetViews of depth 1, the overlay may be converted to an RGB(A)
    SheetMatrix via the rgb property.
    """

    dimensions = param.List(default=[Dimension('Layer')], constant=True, doc="""List
      of dimensions the Overlay can be indexed by.""")

    label = param.String(doc="""
      A short label used to indicate what kind of data is contained
      within the view object.

      Overlays should not have their label set directly by the user as
      the label is only for defining custom channel operations.""")

    title = param.String(default="{label}")

    channels = channels
    _deep_indexable = True

    def __init__(self, overlays, **kwargs):
        self._xlim = None
        self._ylim = None
        data = self._process_layers(overlays)
        super(Overlay, self).__init__(data, **kwargs)
        self._data = self.data


    def _process_layers(self, layers):
        """
        Set a collection of layers to be overlaid with each other.
        """
        if isinstance(layers, ViewMap):
            return layers
        elif layers is None or not len(layers):
            return OrderedDict()
        keys = range(len(layers))
        data = OrderedDict(((key,), layer) for key, layer in zip(keys, layers))
        return data


    def set(self, layers):
        data = self._process_layers(layers)
        self._data = data


    @property
    def labels(self):
        return [el.label for el in self]


    @property
    def legend(self):
        if self.dimension_labels == ['Layer']:
            labels = self.labels
            if len(set(labels)) == len(labels):
                return labels
            else:
                return None
        else:
            labels = []
            for key in self.keys():
                labels.append(','.join([dim.pprint_value(k) for dim, k in
                                        zip(self.dimensions, key)]))
            return labels

{'fraction_non_alphanumeric': 0.10268133911965283, 'fraction_numerical': 0.0034097954122752636, 'mean_word_length': 2.2820447609359107, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5864995', 'n_tokens_mistral': 3834, 'n_tokens_neox': 3669, 'n_words': 1305}