diff --git a/rolling/apply.py b/rolling/apply.py index 914d5ef..7eba9e8 100644 --- a/rolling/apply.py +++ b/rolling/apply.py @@ -49,15 +49,18 @@ class Apply(RollingObject): [5, 6, 3, 1]] """ + def __init__(self, iterable, window_size, window_type="fixed", operation=sum): + super().__init__(iterable, window_size, window_type) + self._operation = operation - def _init_fixed(self, iterable, window_size, operation=sum, **kwargs): + def _init_fixed(self): self._buffer = deque([None]) - self._buffer.extend(islice(self._iterator, window_size - 1)) - self._operation = operation + self._buffer.extend(islice(self._iterator, self.window_size - 1)) - def _init_variable(self, iterable, window_size, operation=sum, **kwargs): + def _init_variable(self): self._buffer = deque() - self._operation = operation + + _init_indexed = _init_variable @property def current_value(self): diff --git a/rolling/apply_pairwise.py b/rolling/apply_pairwise.py index 5d00a3f..16962fa 100644 --- a/rolling/apply_pairwise.py +++ b/rolling/apply_pairwise.py @@ -44,13 +44,13 @@ def __init__(self, iterable_1, iterable_2, window_size, function, window_type="f self._function = function super().__init__(iterable_1, iterable_2, window_size=window_size, window_type=window_type) - def _init_fixed(self, **kwargs): + def _init_fixed(self): pairs = zip(self._iterator_1, self._iterator_2) for item_1, item_2 in islice(pairs, self.window_size-1): self._buffer_1.append(item_1) self._buffer_2.append(item_2) - def _init_variable(self, **kwargs): + def _init_variable(self): pass # no action required @property diff --git a/rolling/arithmetic/nunique.py b/rolling/arithmetic/nunique.py index b857771..286c477 100644 --- a/rolling/arithmetic/nunique.py +++ b/rolling/arithmetic/nunique.py @@ -40,17 +40,19 @@ class Nunique(RollingObject): """ - def _init_fixed(self, iterable, window_size, **kwargs): - head = islice(self._iterator, window_size - 1) + def _init_fixed(self): + head = islice(self._iterator, self.window_size - 1) self._buffer = deque(head) # append a dummy value that is removed when next() is called self._buffer.appendleft("dummy_value") self._counter = Counter(self._buffer) - def _init_variable(self, iterable, window_size, **kwargs): + def _init_variable(self): self._buffer = deque() self._counter = Counter() + _init_indexed = _init_variable + def _update_window(self, new): # remove oldest value before appending new to buffer self._remove_old() diff --git a/rolling/arithmetic/product.py b/rolling/arithmetic/product.py index 5120fdb..99592ec 100644 --- a/rolling/arithmetic/product.py +++ b/rolling/arithmetic/product.py @@ -40,8 +40,8 @@ class Product(RollingObject): """ - def _init_fixed(self, iterable, window_size, **kwargs): - head = islice(self._iterator, window_size - 1) + def _init_fixed(self): + head = islice(self._iterator, self.window_size - 1) self._buffer = deque(head) self._zero_count = 0 @@ -56,11 +56,13 @@ def _init_fixed(self, iterable, window_size, **kwargs): self._buffer.appendleft(1) self._product = prod - def _init_variable(self, iterable, window_size, **kwargs): + def _init_variable(self): self._buffer = deque() self._zero_count = 0 self._product = 1 + _init_indexed = _init_variable + def _update_window(self, new): old = self._buffer.popleft() self._buffer.append(new) diff --git a/rolling/arithmetic/sum.py b/rolling/arithmetic/sum.py index af756f1..ebc902e 100644 --- a/rolling/arithmetic/sum.py +++ b/rolling/arithmetic/sum.py @@ -39,16 +39,18 @@ class Sum(RollingObject): [13, 11, 15] """ - def _init_fixed(self, iterable, window_size, **kwargs): - head = islice(self._iterator, window_size - 1) - self._buffer = deque(head, maxlen=window_size) + def _init_fixed(self): + head = islice(self._iterator, self.window_size - 1) + self._buffer = deque(head, maxlen=self.window_size) self._buffer.appendleft(0) self._sum = sum(self._buffer) - def _init_variable(self, iterable, window_size, **kwargs): + def _init_variable(self): self._buffer = deque() self._sum = 0 + _init_indexed = _init_variable + def _update_window(self, new): self._sum += new - self._buffer.popleft() self._buffer.append(new) diff --git a/rolling/base.py b/rolling/base.py index 67b458f..17fc4c2 100644 --- a/rolling/base.py +++ b/rolling/base.py @@ -31,24 +31,24 @@ class RollingObject(Iterator): """ - def __init__(self, iterable, window_size, window_type="fixed", **kwargs): + def __init__(self, iterable, window_size, window_type="fixed"): self.window_type = window_type self.window_size = _validate_window_size(window_size, window_type) self._iterator = iter(iterable) self._filled = self.window_type == "fixed" if window_type == "fixed": - self._init_fixed(iterable, window_size, **kwargs) + self._init_fixed() elif window_type == "variable": - self._init_variable(iterable, window_size, **kwargs) + self._init_variable() elif window_type == "indexed": # Keep track of all indexes that we encounter. Assumes that all # values we encounter will be stored in the same order. If not, # the subtype will need to implement its own _next_indexed() method. self.index_buffer = deque() - self._init_indexed(iterable, window_size, **kwargs) + self._init_indexed() else: raise ValueError(f"Unknown window_type '{window_type}'") @@ -149,7 +149,7 @@ def _obs(self): """ Return the number of observations in the window """ - pass + return 0 @abc.abstractmethod def _init_fixed(self): @@ -165,13 +165,14 @@ def _init_variable(self): """ pass - def _init_indexed(self, *args, **kwargs): + @abc.abstractmethod + def _init_indexed(self): """ Intialise as an indexed window. In most cases this is the same as initialising a variable-size window. """ - return self._init_variable(*args, **kwargs) + pass @abc.abstractmethod def _remove_old(self): diff --git a/rolling/base_pairwise.py b/rolling/base_pairwise.py index adda361..9d939ef 100644 --- a/rolling/base_pairwise.py +++ b/rolling/base_pairwise.py @@ -8,7 +8,7 @@ class RollingPairwise(Iterator): Baseclass for rolling iterators over two iterables. """ - def __init__(self, iterable_1, iterable_2, window_size, window_type="fixed", **kwargs): + def __init__(self, iterable_1, iterable_2, window_size, window_type="fixed"): self.window_type = window_type self.window_size = _validate_window_size(window_size) self._iterator_1 = iter(iterable_1) @@ -16,10 +16,10 @@ def __init__(self, iterable_1, iterable_2, window_size, window_type="fixed", **k self._filled = self.window_type == "fixed" if window_type == "fixed": - self._init_fixed(**kwargs) + self._init_fixed() elif window_type == "variable": - self._init_variable(**kwargs) + self._init_variable() else: raise ValueError(f"Unknown window_type '{window_type}'") @@ -80,17 +80,17 @@ def _obs(self): """ Return the number of observations in the window """ - pass + return 0 @abc.abstractmethod - def _init_fixed(self, **kwargs): + def _init_fixed(self): """ Intialise as a fixed-size window """ pass @abc.abstractmethod - def _init_variable(self, **kwargs): + def _init_variable(self): """ Intialise as a variable-size window """ @@ -104,14 +104,14 @@ def _remove_old(self): pass @abc.abstractmethod - def _add_new(self, new): + def _add_new(self, new_1, new_2): """ Add a new value to the window, increasing window size by 1 """ pass @abc.abstractmethod - def _update_window(self, new): + def _update_window(self, new_1, new_2): """ Add a new value to the window and remove the oldest value from the window """ diff --git a/rolling/entropy.py b/rolling/entropy.py index 041bcf3..8abc35a 100644 --- a/rolling/entropy.py +++ b/rolling/entropy.py @@ -86,12 +86,12 @@ def __init__(self, iterable, window_size, base=2, reference_distribution=None): self._log = _get_log_func(base) super().__init__(iterable, window_size) - def _init_fixed(self, iterable, window_size, **kwargs): + def _init_fixed(self): self._entropy = 0.0 self._summands = {} - head = islice(self._iterator, window_size - 1) - self._buffer = deque(head, maxlen=window_size) + head = islice(self._iterator, self.window_size - 1) + self._buffer = deque(head, maxlen=self.window_size) counts = Counter(self._buffer) @@ -104,7 +104,7 @@ def _init_fixed(self, iterable, window_size, **kwargs): self._buffer.appendleft(object) self._summands[object] = (1, 0) - def _init_variable(self, iterable, window_size, **kwargs): + def _init_variable(self): raise NotImplementedError("Entropy not implemented for variable windows") def _update_window(self, new): @@ -147,11 +147,11 @@ def _obs(self): return len(self._buffer) # Required, but unused as variable windows not supported - def _add_new(self, new): + def _add_new(self): pass def _remove_old(self): pass - def _init_indexed(self, *args, **kwargs): + def _init_indexed(self): raise NotImplementedError("window_type='indexed'") \ No newline at end of file diff --git a/rolling/hash.py b/rolling/hash.py index 25b9074..28921ba 100644 --- a/rolling/hash.py +++ b/rolling/hash.py @@ -72,12 +72,12 @@ def __init__( self._mod = mod super().__init__(iterable, window_size, window_type) - def _init_fixed(self, *args, **kwargs): + def _init_fixed(self): self._buffer = deque([0]) for val in islice(self._iterator, self.window_size - 1): self._add_new(val) - def _init_variable(self, *args, **kwargs): + def _init_variable(self): self._buffer = deque() def _add_new(self, new): @@ -103,5 +103,5 @@ def current_value(self): def _obs(self): return len(self._buffer) - def _init_indexed(self, *args, **kwargs): + def _init_indexed(self): raise NotImplementedError("window_type='indexed'") diff --git a/rolling/logical/all.py b/rolling/logical/all.py index 827f04a..720d048 100644 --- a/rolling/logical/all.py +++ b/rolling/logical/all.py @@ -39,14 +39,14 @@ class All(RollingObject): """ - def _init_fixed(self, iterable, window_size, **kwargs): + def _init_fixed(self): self._i = -1 self._window_obs = 1 self._last_false = -1 - for new in islice(self._iterator, window_size - 1): + for new in islice(self._iterator, self.window_size - 1): self._add_new(new) - def _init_variable(self, iterable, window_size, **kwargs): + def _init_variable(self): self._i = -1 self._window_obs = 0 self._last_false = -1 @@ -73,6 +73,6 @@ def _obs(self): def current_value(self): return self._i - self._window_obs >= self._last_false - def _init_indexed(self, *args, **kwargs): + def _init_indexed(self): raise NotImplementedError("window_type='indexed'") diff --git a/rolling/logical/any.py b/rolling/logical/any.py index 1754b06..dc9bacf 100644 --- a/rolling/logical/any.py +++ b/rolling/logical/any.py @@ -39,14 +39,14 @@ class Any(RollingObject): """ - def _init_fixed(self, iterable, window_size, **kwargs): + def _init_fixed(self): self._i = -1 self._window_obs = 1 self._last_true = -1 - for new in islice(self._iterator, window_size - 1): + for new in islice(self._iterator, self.window_size - 1): self._add_new(new) - def _init_variable(self, iterable, window_size, **kwargs): + def _init_variable(self): self._i = -1 self._window_obs = 0 self._last_true = -1 @@ -73,5 +73,5 @@ def _obs(self): def current_value(self): return self._i - self._window_obs < self._last_true - def _init_indexed(self, *args, **kwargs): + def _init_indexed(self): raise NotImplementedError("window_type='indexed'") \ No newline at end of file diff --git a/rolling/minmax.py b/rolling/minmax.py index 6b361ee..ae4f222 100644 --- a/rolling/minmax.py +++ b/rolling/minmax.py @@ -44,14 +44,14 @@ class Min(RollingObject): # the size of the buffer as the algorithm may overwrite existing # values with a new value, rather than appending the value - def _init_fixed(self, iterable, window_size, **kwargs): + def _init_fixed(self): self._i = -1 self._window_obs = 0 self._buffer = deque() - for new in islice(self._iterator, window_size - 1): + for new in islice(self._iterator, self.window_size - 1): self._add_new(new) - def _init_variable(self, iterable, window_size, **kwargs): + def _init_variable(self): self._i = -1 self._window_obs = 0 self._buffer = deque() @@ -91,7 +91,7 @@ def _obs(self): def current_value(self): return _value(self._buffer[0]) - def _init_indexed(self, *args, **kwargs): + def _init_indexed(self): raise NotImplementedError("window_type='indexed'") @@ -129,14 +129,14 @@ class Max(RollingObject): # the size of the buffer as the algorithm may overwrite existing # values with a new value, rather than appending the value - def _init_fixed(self, iterable, window_size, **kwargs): + def _init_fixed(self): self._i = -1 self._window_obs = 0 self._buffer = deque() - for new in islice(self._iterator, window_size - 1): + for new in islice(self._iterator, self.window_size - 1): self._add_new(new) - def _init_variable(self, iterable, window_size, **kwargs): + def _init_variable(self): self._buffer = deque() self._i = -1 self._window_obs = 0 @@ -176,7 +176,7 @@ def _obs(self): def current_value(self): return _value(self._buffer[0]) - def _init_indexed(self, *args, **kwargs): + def _init_indexed(self): raise NotImplementedError("window_type='indexed'") class MinHeap(RollingObject): @@ -211,15 +211,15 @@ class MinHeap(RollingObject): window size, k, in cases where data is ordered. """ - def _init_fixed(self, iterable, window_size, **kwargs): - head = islice(self._iterator, window_size - 1) + def _init_fixed(self): + head = islice(self._iterator, self.window_size - 1) # faster to create the heap this way, rather than repeat _add_new() - self._heap = [(value, i + window_size) for i, value in enumerate(head)] + self._heap = [(value, i + self.window_size) for i, value in enumerate(head)] heapify(self._heap) self._i = len(self._heap) - 1 self._window_obs = len(self._heap) - def _init_variable(self, iterable, window_size, **kwargs): + def _init_variable(self): self._heap = [] self._i = -1 self._window_obs = 0 @@ -253,5 +253,5 @@ def _obs(self): def current_value(self): return _value(self._heap[0]) - def _init_indexed(self, *args, **kwargs): + def _init_indexed(self): raise NotImplementedError("window_type='indexed'") \ No newline at end of file diff --git a/rolling/similarity.py b/rolling/similarity.py index 3d71222..21f9b83 100644 --- a/rolling/similarity.py +++ b/rolling/similarity.py @@ -65,14 +65,16 @@ def __init__(self, iterable, window_size, target_set, window_type="fixed"): self._union = Counter(self._target_set) super().__init__(iterable, window_size, window_type) - def _init_fixed(self, *args, **kwargs): + def _init_fixed(self): self._buffer.append(None) for val in islice(self._iterator, self.window_size - 1): self._add_new(val) - def _init_variable(self, *args, **kwargs): + def _init_variable(self): pass + _init_indexed = _init_variable + def _add_new(self, new): self._buffer.append(new) self._union[new] += 1 diff --git a/rolling/stats/kurtosis.py b/rolling/stats/kurtosis.py index 55d2414..3877721 100644 --- a/rolling/stats/kurtosis.py +++ b/rolling/stats/kurtosis.py @@ -35,34 +35,34 @@ class Kurtosis(RollingObject): """ - def _init_fixed(self, iterable, window_size, **kwargs): - if window_size <= 3: + def _init_fixed(self): + if self.window_size <= 3: raise ValueError("window_size must be greater than 3") - self._buffer = deque(maxlen=window_size) + self._buffer = deque(maxlen=self.window_size) self._x1 = 0.0 self._x2 = 0.0 self._x3 = 0.0 self._x4 = 0.0 - for new in islice(self._iterator, window_size - 1): + for new in islice(self._iterator, self.window_size - 1): self._add_new(new) # insert zero at the start of the buffer so that the # the first call to update returns the correct value self._buffer.appendleft(0) - def _init_variable(self, iterable, window_size, **kwargs): - if window_size <= 3: + def _init_variable(self): + if self.window_size <= 3: raise ValueError("window_size must be greater than 3") - self._buffer = deque(maxlen=window_size) + self._buffer = deque(maxlen=self.window_size) self._x1 = 0.0 self._x2 = 0.0 self._x3 = 0.0 self._x4 = 0.0 - def _init_indexed(self, iterable, window_size, **kwargs): + def _init_indexed(self): self._buffer = deque() self._x1 = 0.0 self._x2 = 0.0 diff --git a/rolling/stats/median.py b/rolling/stats/median.py index 4c933ae..0b46284 100644 --- a/rolling/stats/median.py +++ b/rolling/stats/median.py @@ -56,8 +56,6 @@ def __init__( ): self._buffer = deque() - - if tracker == "sortedlist": self._tracker = SortedList() elif tracker == "skiplist": @@ -67,9 +65,9 @@ def __init__( super().__init__(iterable, window_size, window_type) - def _init_fixed(self, iterable, window_size, **kwargs): + def _init_fixed(self): # update buffer and skiplist with initial values - for new in islice(self._iterator, window_size - 1): + for new in islice(self._iterator, self.window_size - 1): self._add_new(new) try: @@ -83,10 +81,12 @@ def _init_fixed(self, iterable, window_size, **kwargs): self._buffer.appendleft(0) self._tracker.insert(0) - def _init_variable(self, iterable, window_size, **kwargs): + def _init_variable(self): # no further initialisation required for variable-size windows pass + _init_indexed = _init_variable + def _update_window(self, new): old = self._buffer.popleft() self._tracker.remove(old) diff --git a/rolling/stats/mode.py b/rolling/stats/mode.py index 9c5d372..cbd563b 100644 --- a/rolling/stats/mode.py +++ b/rolling/stats/mode.py @@ -45,22 +45,24 @@ class Mode(RollingObject): is not unique. """ - - def _init_fixed(self, iterable, window_size, return_count=False, **kwargs): - self._buffer = deque(maxlen=window_size) + def __init__(self, iterable, window_size, window_type="fixed", return_count=False): self.return_count = return_count self._bicounter = BiCounter() - for item in islice(self._iterator, window_size - 1): + super().__init__(iterable, window_size, window_type) + + def _init_fixed(self): + self._buffer = deque(maxlen=self.window_size) + for item in islice(self._iterator, self.window_size - 1): self._add_new(item) # insert a value to be removed on the first call to update self._buffer.appendleft("DUMMY_VALUE") self._bicounter.increment("DUMMY_VALUE") - def _init_variable(self, iterable, window_size, return_count=False, **kwargs): + def _init_variable(self): self._buffer = deque() - self.return_count = return_count - self._bicounter = BiCounter() + + _init_indexed = _init_variable def _update_window(self, new): old = self._buffer.popleft() diff --git a/rolling/stats/skew.py b/rolling/stats/skew.py index ab62566..82e8672 100644 --- a/rolling/stats/skew.py +++ b/rolling/stats/skew.py @@ -36,32 +36,32 @@ class Skew(RollingObject): """ - def _init_fixed(self, iterable, window_size, **kwargs): - if window_size <= 2: + def _init_fixed(self): + if self.window_size <= 2: raise ValueError("window_size must be greater than 2") - self._buffer = deque(maxlen=window_size) + self._buffer = deque(maxlen=self.window_size) self._x1 = 0.0 self._x2 = 0.0 self._x3 = 0.0 - for new in islice(self._iterator, window_size - 1): + for new in islice(self._iterator, self.window_size - 1): self._add_new(new) # insert zero at the start of the buffer so that the # the first call to update returns the correct value self._buffer.appendleft(0) - def _init_variable(self, iterable, window_size, **kwargs): - if window_size <= 2: + def _init_variable(self): + if self.window_size <= 2: raise ValueError("window_size must be greater than 2") - self._buffer = deque(maxlen=window_size) + self._buffer = deque(maxlen=self.window_size) self._x1 = 0.0 self._x2 = 0.0 self._x3 = 0.0 - def _init_indexed(self, iterable, window_size, **kwargs): + def _init_indexed(self): self._buffer = deque() self._x1 = 0.0 self._x2 = 0.0 diff --git a/rolling/stats/variance.py b/rolling/stats/variance.py index 614b77f..d5b2eda 100644 --- a/rolling/stats/variance.py +++ b/rolling/stats/variance.py @@ -39,48 +39,38 @@ class Var(RollingObject): windows), the variance is computed as NaN. """ - - def _init_fixed(self, iterable, window_size, ddof=1, **kwargs): - if window_size <= ddof: - raise ValueError("window_size must be greater than ddof") - + def __init__(self, iterable, window_size, window_type="fixed", ddof=1): self.ddof = ddof - self._buffer = deque(maxlen=window_size) self._mean = 0.0 # mean of values self._sslm = 0.0 # sum of squared values less the mean + super().__init__(iterable, window_size, window_type) + + def _init_fixed(self): + if self.window_size <= self.ddof: + raise ValueError("window_size must be greater than ddof") - for new in islice(self._iterator, window_size - 1): + self._buffer = deque(maxlen=self.window_size) + for new in islice(self._iterator, self.window_size - 1): self._add_new(new) # insert mean at the start of the buffer so that the # the first call to update returns the correct value self._buffer.appendleft(self._mean) - def _init_variable(self, iterable, window_size, ddof=1, **kwargs): - if window_size <= ddof: - raise ValueError("window_size must be greater than ddof") - - self.ddof = ddof - self._buffer = deque(maxlen=window_size) - self._mean = 0.0 # mean of values - self._sslm = 0.0 # sum of squared values less the mean + def _init_variable(self): + self._buffer = deque(maxlen=self.window_size) - def _init_indexed(self, iterable, window_size, ddof=1, **kwargs): - self.ddof = ddof + def _init_indexed(self): self._buffer = deque() - self._mean = 0.0 # mean of values - self._sslm = 0.0 # sum of squared values less the mean def _add_new(self, new): self._buffer.append(new) - delta = new - self._mean self._mean += delta / self._obs self._sslm += delta * (new - self._mean) def _remove_old(self): old = self._buffer.popleft() - delta = old - self._mean self._mean -= delta / self._obs self._sslm -= delta * (old - self._mean) @@ -88,7 +78,6 @@ def _remove_old(self): def _update_window(self, new): old = self._buffer[0] self._buffer.append(new) - delta = new - old delta_old = old - self._mean self._mean += delta / self._obs