<commit_before>import numpy as np
from functools import lru_cache


class Events(object):
    def __init__(self, condition, sample_rate=1,
                 entry_debounce=0, exit_debounce=0,
                 min_event_length=0, max_event_length=None):

        self.condition = condition
        self.sample_rate = sample_rate  # Assumes univariate time series
        self.entry_debounce = entry_debounce
        self.exit_debounce = exit_debounce
        self.min_event_length = min_event_length
        self.max_event_length = max_event_length

        self.starts, self.stops = self._apply_filters()
        # TODO - work out strategy for multivariate data
        # TODO - potentially just return a tuple of (start, stop) values

    @property
    def size(self):
        """
        Return the number of events found.
        """
        return self.starts.size

    @lru_cache(10)
    def as_array(self, false_values=0, true_values=1, dtype='float'):
        """
        Return the found events as a numpy array of 0's and 1'sample_rate
        """
        # TODO - Cache this value? or make it a method (better option)

        output = np.ones(self.condition.size, dtype=dtype) * false_values
        for start, stop in zip(self.starts, self.stops):
            output[start:stop] = 1 * true_values
        return output

    def _apply_filters(self):
        starts, stops = self._apply_condition_filter()

        if starts.size > 0 and (self.entry_debounce or self.exit_debounce):
            starts, stops = self._apply_debounce_filter(starts, stops)

        if starts.size > 0 and (self.min_event_length or self.max_event_length):
            starts, stops = self._apply_event_length_filter(starts, stops)

        return starts, stops

    def _apply_condition_filter(self):
        """
        Apply initial masking conditions
        """
        mask = (self.condition > 0).view('i1')
        slice_index = np.arange(mask.size + 1, dtype='int32')

        if mask[0] == 0:
            to_begin = np.array([0], dtype='i1')
        else:
            to_begin = np.array([1], dtype='i1')

        if mask[-1] == 0:
            to_end = np.array([0], dtype='i1')
        else:
            to_end = np.array([-1], dtype='i1')

        deltas = np.ediff1d(mask, to_begin=to_begin, to_end=to_end)

        starts = np.ma.masked_where(deltas < 1, slice_index).compressed()
        stops = np.ma.masked_where(deltas > -1, slice_index).compressed()

        return starts, stops

    def _apply_debounce_filter(self, starts, stops):
        """ Apply debounce paramaters"""
        # TODO - Replace with function in Cython for significant speed boost
        start_mask = np.zeros(starts.size)
        stop_mask = np.zeros(stops.size)
        event_active = False

        for index in np.arange(starts.size):
            event_length = stops[index] - starts[index]
<commit_msg>Convert starts and stops attributes to lazily evaluated properties<commit_after>import numpy as np
from functools import lru_cache


def lazyproperty(func):
    name = '_lazy_' + func.__name__

    @property
    def lazy(self):
        if hasattr(self, name):
            return getattr(self, name)
        else:
            value = func(self)
            setattr(self, name, value)
            return value
    return lazy


class Events(object):
    def __init__(self, condition, sample_rate=1,
                 entry_debounce=0, exit_debounce=0,
                 min_event_length=0, max_event_length=None):

        self.condition = condition
        self.sample_rate = sample_rate  # Assumes univariate time series
        self.entry_debounce = entry_debounce
        self.exit_debounce = exit_debounce
        self.min_event_length = min_event_length
        self.max_event_length = max_event_length

        # TODO - work out strategy for multivariate data

    @property
    def size(self):
        """Return the number of events found."""
        return self.condition.size
        
    @lazyproperty
    def starts(self):
        """Return a numpy.array() of start indexes."""
        starts, _ = self._apply_filters()
        return starts
        
    @lazyproperty
    def stops(self):
        """Return a numpy.array() of start indexes."""
        _, stops = self._apply_filters()
        return stops

    @lru_cache(10)
    def as_array(self, false_values=0, true_values=1, dtype='float'):
        """
        Return the found events as a numpy array of 0's and 1'sample_rate
        """
        # TODO - Cache this value? or make it a method (better option)
        starts = self.starts
        stops = self.stops
        output = np.ones(self.condition.size, dtype=dtype) * false_values
        for start, stop in zip(starts, stops):
            output[start:stop] = 1 * true_values
        return output

    @lru_cache(1)
    def _apply_filters(self):
        starts, stops = self._apply_condition_filter()

        if starts.size > 0 and (self.entry_debounce or self.exit_debounce):
            starts, stops = self._apply_debounce_filter(starts, stops)

        if starts.size > 0 and (self.min_event_length or self.max_event_length):
            starts, stops = self._apply_event_length_filter(starts, stops)

        return starts, stops

    def _apply_condition_filter(self):
        """
        Apply initial masking conditions
        """
        mask = (self.condition > 0).view('i1')
        slice_index = np.arange(mask.size + 1, dtype='int32')

        if mask[0] == 0:
            to_begin = np.array([0], dtype='i1')
        else:
            to_begin = np.array([1], dtype='i1')

        if mask[-1] == 0:
            to_end = np.array([0], dtype='i1')
        else:
            to_end = np.array([-1], dtype='i1')

        deltas = np.ediff1d(mask, to_begin=to_begin, to_end=to_end)

        starts = np.ma.masked_where(deltas < 1, slice_index).compressed()
        stops = np.ma.masked_where(deltas > -1, slice_index).compressed()

        return starts, stops

    def _apply_debounce_filter(self, starts, stops):
        """ Apply debounce parameters"""
        # TODO - Replace with function in Cython for significant speed boost
        start_mask = np.zeros(starts.size)
        stop_mask = np.zeros(stops.size)
        event_active = False

        for index in np.arange(starts.size):
            event_length = stops[index] - starts[index]

{'fraction_non_alphanumeric': 0.10047095761381476, 'fraction_numerical': 0.010518053375196233, 'mean_word_length': 2.3356020942408375, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2621780', 'n_tokens_mistral': 1982, 'n_tokens_neox': 1893, 'n_words': 484}