From 9bcc5df0be22633892ebd162ce074b10306fbf21 Mon Sep 17 00:00:00 2001 From: Piotr Maslanka <piotr.maslanka@henrietta.com.pl> Date: Sat, 9 Dec 2017 08:52:22 +0100 Subject: [PATCH] refactor --- README.md | 8 +++ firanka/series/__init__.py | 14 +++++ firanka/{series.py => series/base.py} | 77 +-------------------------- firanka/series/linear.py | 45 ++++++++++++++++ firanka/series/modulo.py | 39 ++++++++++++++ 5 files changed, 107 insertions(+), 76 deletions(-) create mode 100644 firanka/series/__init__.py rename firanka/{series.py => series/base.py} (77%) create mode 100644 firanka/series/linear.py create mode 100644 firanka/series/modulo.py diff --git a/README.md b/README.md index 479fe21..97a694c 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,14 @@ By definition, _ModuloSeries_ has the domain of all real numbers. Note that someOtherSeries's domain length must be non-zero and finite. Otherwise _ValueError_ will be thrown. +## LinearInterpolationSeries + +These are discretes, but allow you to define an operator that will +take its neighbours into account and let you return a custom value. + +By default, it will assumes that values can be added, subbed, multed and dived, +and will do classical linear interpolation. + ## Ranges Can be imported from _sai.ranges_. diff --git a/firanka/series/__init__.py b/firanka/series/__init__.py new file mode 100644 index 0000000..0a890c4 --- /dev/null +++ b/firanka/series/__init__.py @@ -0,0 +1,14 @@ +# coding=UTF-8 +from __future__ import absolute_import +from .base import FunctionSeries, DiscreteSeries, Series +from .linear import LinearInterpolationSeries, SCALAR_LINEAR_INTERPOLATOR +from .modulo import ModuloSeries + +__all__ = [ + 'FunctionSeries', + 'DiscreteSeries', + 'ModuloSeries', + 'Series', + 'SCALAR_LINEAR_INTERPOLATOR', + 'LinearInterpolationSeries', +] diff --git a/firanka/series.py b/firanka/series/base.py similarity index 77% rename from firanka/series.py rename to firanka/series/base.py index 606a187..52a6d16 100644 --- a/firanka/series.py +++ b/firanka/series/base.py @@ -8,14 +8,7 @@ import six from firanka.exceptions import NotInDomainError from firanka.ranges import Range, REAL_SET, EMPTY_SET -__all__ = [ - 'FunctionSeries', - 'DiscreteSeries', - 'ModuloSeries', - 'Series', - 'SCALAR_LINEAR_INTERPOLATOR', - 'LinearInterpolationSeries', -] + class Series(object): @@ -289,71 +282,3 @@ class JoinedSeries(Series): def _get_for(self, item): return self.op(self.ser1._get_for(item), self.ser2._get_for(item)) - -class ModuloSeries(Series): - def __init__(self, series, *args, **kwargs): - """ - Construct a modulo series - :param series: base series to use - :raise ValueError: invalid domain length - """ - super(ModuloSeries, self).__init__(REAL_SET, *args, **kwargs) - - self.series = series - self.period = self.series.domain.length() - - if self.period == 0: - raise ValueError('Modulo series cannot have a period of 0') - elif math.isinf(self.period): - raise ValueError('Modulo series cannot have an infinite period') - - def _get_for(self, item): - if item < 0: - item = -(item // self.period) * self.period + item - elif item > self.period: - item = item - (item // self.period) * self.period - elif item == self.period: - item = 0 - - return self.series._get_for(self.series.domain.start + item) - - -def SCALAR_LINEAR_INTERPOLATOR(t0, v0, t1, v1, tt): - """ - Good intepolator if our values can be added, subtracted, multiplied and divided - """ - return v0 + (tt-t0) * (t1-t0)/(v1-v0) - - -class LinearInterpolationSeries(DiscreteSeries): - - def __init__(self, data, domain=None, - interpolator=lambda t0, v0, t1, v1, tt: v0 - , *args, **kwargs): - """ - :param interpolator: callable(t0: float, v0: any, t1: float, v1: any, tt: float) -> any - This, given intepolation points (t0, v0) and (t1, v1) such that t0 <= tt <= t1, - return a value for index tt - """ - self.interpolator = interpolator - if isinstance(data, DiscreteSeries): - self.data = data.data - self.domain = domain or data.domain - else: - super(LinearInterpolationSeries, self).__init__(data, domain, *args, **kwargs) - - def _get_for(self, item): - if item == self.domain.start: - return self.data[0][1] - - if len(self.data) == 1: - return super(LinearInterpolationSeries, self).__getitem__(item) - - for i in six.moves.range(0, len(self.data)-1): - cur_i, cur_v = self.data[i] - next_i, next_v = self.data[i+1] - - if cur_i <= item <= next_i: - return self.interpolator(cur_i, cur_v, next_i, next_v, item) - - return self.data[-1][1] diff --git a/firanka/series/linear.py b/firanka/series/linear.py new file mode 100644 index 0000000..5814bcb --- /dev/null +++ b/firanka/series/linear.py @@ -0,0 +1,45 @@ +# coding=UTF-8 +from __future__ import print_function, absolute_import, division +import six + +from .base import DiscreteSeries + + +def SCALAR_LINEAR_INTERPOLATOR(t0, v0, t1, v1, tt): + """ + Good intepolator if our values can be added, subtracted, multiplied and divided + """ + return v0 + (tt-t0) * (t1-t0)/(v1-v0) + + +class LinearInterpolationSeries(DiscreteSeries): + + def __init__(self, data, domain=None, interpolator=SCALAR_LINEAR_INTERPOLATOR, + *args, **kwargs): + """ + :param interpolator: callable(t0: float, v0: any, t1: float, v1: any, tt: float) -> any + This, given intepolation points (t0, v0) and (t1, v1) such that t0 <= tt <= t1, + return a value for index tt + """ + self.interpolator = interpolator + if isinstance(data, DiscreteSeries): + self.data = data.data + self.domain = domain or data.domain + else: + super(LinearInterpolationSeries, self).__init__(data, domain, *args, **kwargs) + + def _get_for(self, item): + if item == self.domain.start: + return self.data[0][1] + + if len(self.data) == 1: + return super(LinearInterpolationSeries, self).__getitem__(item) + + for i in six.moves.range(0, len(self.data)-1): + cur_i, cur_v = self.data[i] + next_i, next_v = self.data[i+1] + + if cur_i <= item <= next_i: + return self.interpolator(cur_i, cur_v, next_i, next_v, item) + + return self.data[-1][1] diff --git a/firanka/series/modulo.py b/firanka/series/modulo.py new file mode 100644 index 0000000..665ea72 --- /dev/null +++ b/firanka/series/modulo.py @@ -0,0 +1,39 @@ +# coding=UTF-8 +from __future__ import print_function, absolute_import, division +import six +import logging +import math + +from .base import Series +from ..ranges import REAL_SET + + +class ModuloSeries(Series): + def __init__(self, series, *args, **kwargs): + """ + Construct a modulo series + :param series: base series to use + :raise ValueError: invalid domain length + """ + super(ModuloSeries, self).__init__(REAL_SET, *args, **kwargs) + + self.series = series + self.period = self.series.domain.length() + + if self.period == 0: + raise ValueError('Modulo series cannot have a period of 0') + elif math.isinf(self.period): + raise ValueError('Modulo series cannot have an infinite period') + + def _get_for(self, item): + if item < 0: + item = -(item // self.period) * self.period + item + elif item > self.period: + item = item - (item // self.period) * self.period + elif item == self.period: + item = 0 + + return self.series._get_for(self.series.domain.start + item) + + + -- GitLab