Chris@87: """ Chris@87: Abstract base class for the various polynomial Classes. Chris@87: Chris@87: The ABCPolyBase class provides the methods needed to implement the common API Chris@87: for the various polynomial classes. It operates as a mixin, but uses the Chris@87: abc module from the stdlib, hence it is only available for Python >= 2.6. Chris@87: Chris@87: """ Chris@87: from __future__ import division, absolute_import, print_function Chris@87: Chris@87: from abc import ABCMeta, abstractmethod, abstractproperty Chris@87: from numbers import Number Chris@87: Chris@87: import numpy as np Chris@87: from . import polyutils as pu Chris@87: Chris@87: __all__ = ['ABCPolyBase'] Chris@87: Chris@87: class ABCPolyBase(object): Chris@87: """An abstract base class for series classes. Chris@87: Chris@87: ABCPolyBase provides the standard Python numerical methods Chris@87: '+', '-', '*', '//', '%', 'divmod', '**', and '()' along with the Chris@87: methods listed below. Chris@87: Chris@87: .. versionadded:: 1.9.0 Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: coef : array_like Chris@87: Series coefficients in order of increasing degree, i.e., Chris@87: ``(1, 2, 3)`` gives ``1*P_0(x) + 2*P_1(x) + 3*P_2(x)``, where Chris@87: ``P_i`` is the basis polynomials of degree ``i``. Chris@87: domain : (2,) array_like, optional Chris@87: Domain to use. The interval ``[domain[0], domain[1]]`` is mapped Chris@87: to the interval ``[window[0], window[1]]`` by shifting and scaling. Chris@87: The default value is the derived class domain. Chris@87: window : (2,) array_like, optional Chris@87: Window, see domain for its use. The default value is the Chris@87: derived class window. Chris@87: Chris@87: Attributes Chris@87: ---------- Chris@87: coef : (N,) ndarray Chris@87: Series coefficients in order of increasing degree. Chris@87: domain : (2,) ndarray Chris@87: Domain that is mapped to window. Chris@87: window : (2,) ndarray Chris@87: Window that domain is mapped to. Chris@87: Chris@87: Class Attributes Chris@87: ---------------- Chris@87: maxpower : int Chris@87: Maximum power allowed, i.e., the largest number ``n`` such that Chris@87: ``p(x)**n`` is allowed. This is to limit runaway polynomial size. Chris@87: domain : (2,) ndarray Chris@87: Default domain of the class. Chris@87: window : (2,) ndarray Chris@87: Default window of the class. Chris@87: Chris@87: """ Chris@87: __metaclass__ = ABCMeta Chris@87: Chris@87: # Not hashable Chris@87: __hash__ = None Chris@87: Chris@87: # Don't let participate in array operations. Value doesn't matter. Chris@87: __array_priority__ = 1000 Chris@87: Chris@87: # Limit runaway size. T_n^m has degree n*m Chris@87: maxpower = 100 Chris@87: Chris@87: @abstractproperty Chris@87: def domain(self): Chris@87: pass Chris@87: Chris@87: @abstractproperty Chris@87: def window(self): Chris@87: pass Chris@87: Chris@87: @abstractproperty Chris@87: def nickname(self): Chris@87: pass Chris@87: Chris@87: @abstractmethod Chris@87: def _add(self): Chris@87: pass Chris@87: Chris@87: @abstractmethod Chris@87: def _sub(self): Chris@87: pass Chris@87: Chris@87: @abstractmethod Chris@87: def _mul(self): Chris@87: pass Chris@87: Chris@87: @abstractmethod Chris@87: def _div(self): Chris@87: pass Chris@87: Chris@87: @abstractmethod Chris@87: def _pow(self): Chris@87: pass Chris@87: Chris@87: @abstractmethod Chris@87: def _val(self): Chris@87: pass Chris@87: Chris@87: @abstractmethod Chris@87: def _int(self): Chris@87: pass Chris@87: Chris@87: @abstractmethod Chris@87: def _der(self): Chris@87: pass Chris@87: Chris@87: @abstractmethod Chris@87: def _fit(self): Chris@87: pass Chris@87: Chris@87: @abstractmethod Chris@87: def _line(self): Chris@87: pass Chris@87: Chris@87: @abstractmethod Chris@87: def _roots(self): Chris@87: pass Chris@87: Chris@87: @abstractmethod Chris@87: def _fromroots(self): Chris@87: pass Chris@87: Chris@87: def has_samecoef(self, other): Chris@87: """Check if coefficients match. Chris@87: Chris@87: .. versionadded:: 1.6.0 Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: other : class instance Chris@87: The other class must have the ``coef`` attribute. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: bool : boolean Chris@87: True if the coefficients are the same, False otherwise. Chris@87: Chris@87: """ Chris@87: if len(self.coef) != len(other.coef): Chris@87: return False Chris@87: elif not np.all(self.coef == other.coef): Chris@87: return False Chris@87: else: Chris@87: return True Chris@87: Chris@87: def has_samedomain(self, other): Chris@87: """Check if domains match. Chris@87: Chris@87: .. versionadded:: 1.6.0 Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: other : class instance Chris@87: The other class must have the ``domain`` attribute. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: bool : boolean Chris@87: True if the domains are the same, False otherwise. Chris@87: Chris@87: """ Chris@87: return np.all(self.domain == other.domain) Chris@87: Chris@87: def has_samewindow(self, other): Chris@87: """Check if windows match. Chris@87: Chris@87: .. versionadded:: 1.6.0 Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: other : class instance Chris@87: The other class must have the ``window`` attribute. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: bool : boolean Chris@87: True if the windows are the same, False otherwise. Chris@87: Chris@87: """ Chris@87: return np.all(self.window == other.window) Chris@87: Chris@87: def has_sametype(self, other): Chris@87: """Check if types match. Chris@87: Chris@87: .. versionadded:: 1.7.0 Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: other : object Chris@87: Class instance. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: bool : boolean Chris@87: True if other is same class as self Chris@87: Chris@87: """ Chris@87: return isinstance(other, self.__class__) Chris@87: Chris@87: def _get_coefficients(self, other): Chris@87: """Interpret other as polynomial coefficients. Chris@87: Chris@87: The `other` argument is checked to see if it is of the same Chris@87: class as self with identical domain and window. If so, Chris@87: return its coefficients, otherwise return `other`. Chris@87: Chris@87: .. versionadded:: 1.9.0 Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: other : anything Chris@87: Object to be checked. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: coef: Chris@87: The coefficients of`other` if it is a compatible instance, Chris@87: of ABCPolyBase, otherwise `other`. Chris@87: Chris@87: Raises Chris@87: ------ Chris@87: TypeError: Chris@87: When `other` is an incompatible instance of ABCPolyBase. Chris@87: Chris@87: """ Chris@87: if isinstance(other, ABCPolyBase): Chris@87: if not isinstance(other, self.__class__): Chris@87: raise TypeError("Polynomial types differ") Chris@87: elif not np.all(self.domain == other.domain): Chris@87: raise TypeError("Domains differ") Chris@87: elif not np.all(self.window == other.window): Chris@87: raise TypeError("Windows differ") Chris@87: return other.coef Chris@87: return other Chris@87: Chris@87: def __init__(self, coef, domain=None, window=None): Chris@87: [coef] = pu.as_series([coef], trim=False) Chris@87: self.coef = coef Chris@87: Chris@87: if domain is not None: Chris@87: [domain] = pu.as_series([domain], trim=False) Chris@87: if len(domain) != 2: Chris@87: raise ValueError("Domain has wrong number of elements.") Chris@87: self.domain = domain Chris@87: Chris@87: if window is not None: Chris@87: [window] = pu.as_series([window], trim=False) Chris@87: if len(window) != 2: Chris@87: raise ValueError("Window has wrong number of elements.") Chris@87: self.window = window Chris@87: Chris@87: def __repr__(self): Chris@87: format = "%s(%s, %s, %s)" Chris@87: coef = repr(self.coef)[6:-1] Chris@87: domain = repr(self.domain)[6:-1] Chris@87: window = repr(self.window)[6:-1] Chris@87: name = self.__class__.__name__ Chris@87: return format % (name, coef, domain, window) Chris@87: Chris@87: def __str__(self): Chris@87: format = "%s(%s)" Chris@87: coef = str(self.coef) Chris@87: name = self.nickname Chris@87: return format % (name, coef) Chris@87: Chris@87: # Pickle and copy Chris@87: Chris@87: def __getstate__(self): Chris@87: ret = self.__dict__.copy() Chris@87: ret['coef'] = self.coef.copy() Chris@87: ret['domain'] = self.domain.copy() Chris@87: ret['window'] = self.window.copy() Chris@87: return ret Chris@87: Chris@87: def __setstate__(self, dict): Chris@87: self.__dict__ = dict Chris@87: Chris@87: # Call Chris@87: Chris@87: def __call__(self, arg): Chris@87: off, scl = pu.mapparms(self.domain, self.window) Chris@87: arg = off + scl*arg Chris@87: return self._val(arg, self.coef) Chris@87: Chris@87: def __iter__(self): Chris@87: return iter(self.coef) Chris@87: Chris@87: def __len__(self): Chris@87: return len(self.coef) Chris@87: Chris@87: # Numeric properties. Chris@87: Chris@87: def __neg__(self): Chris@87: return self.__class__(-self.coef, self.domain, self.window) Chris@87: Chris@87: def __pos__(self): Chris@87: return self Chris@87: Chris@87: def __add__(self, other): Chris@87: try: Chris@87: othercoef = self._get_coefficients(other) Chris@87: coef = self._add(self.coef, othercoef) Chris@87: except TypeError as e: Chris@87: raise e Chris@87: except: Chris@87: return NotImplemented Chris@87: return self.__class__(coef, self.domain, self.window) Chris@87: Chris@87: def __sub__(self, other): Chris@87: try: Chris@87: othercoef = self._get_coefficients(other) Chris@87: coef = self._sub(self.coef, othercoef) Chris@87: except TypeError as e: Chris@87: raise e Chris@87: except: Chris@87: return NotImplemented Chris@87: return self.__class__(coef, self.domain, self.window) Chris@87: Chris@87: def __mul__(self, other): Chris@87: try: Chris@87: othercoef = self._get_coefficients(other) Chris@87: coef = self._mul(self.coef, othercoef) Chris@87: except TypeError as e: Chris@87: raise e Chris@87: except: Chris@87: return NotImplemented Chris@87: return self.__class__(coef, self.domain, self.window) Chris@87: Chris@87: def __div__(self, other): Chris@87: # set to __floordiv__, /, for now. Chris@87: return self.__floordiv__(other) Chris@87: Chris@87: def __truediv__(self, other): Chris@87: # there is no true divide if the rhs is not a Number, although it Chris@87: # could return the first n elements of an infinite series. Chris@87: # It is hard to see where n would come from, though. Chris@87: if not isinstance(other, Number) or isinstance(other, bool): Chris@87: form = "unsupported types for true division: '%s', '%s'" Chris@87: raise TypeError(form % (type(self), type(other))) Chris@87: return self.__floordiv__(other) Chris@87: Chris@87: def __floordiv__(self, other): Chris@87: res = self.__divmod__(other) Chris@87: if res is NotImplemented: Chris@87: return res Chris@87: return res[0] Chris@87: Chris@87: def __mod__(self, other): Chris@87: res = self.__divmod__(other) Chris@87: if res is NotImplemented: Chris@87: return res Chris@87: return res[1] Chris@87: Chris@87: def __divmod__(self, other): Chris@87: try: Chris@87: othercoef = self._get_coefficients(other) Chris@87: quo, rem = self._div(self.coef, othercoef) Chris@87: except (TypeError, ZeroDivisionError) as e: Chris@87: raise e Chris@87: except: Chris@87: return NotImplemented Chris@87: quo = self.__class__(quo, self.domain, self.window) Chris@87: rem = self.__class__(rem, self.domain, self.window) Chris@87: return quo, rem Chris@87: Chris@87: def __pow__(self, other): Chris@87: coef = self._pow(self.coef, other, maxpower=self.maxpower) Chris@87: res = self.__class__(coef, self.domain, self.window) Chris@87: return res Chris@87: Chris@87: def __radd__(self, other): Chris@87: try: Chris@87: coef = self._add(other, self.coef) Chris@87: except: Chris@87: return NotImplemented Chris@87: return self.__class__(coef, self.domain, self.window) Chris@87: Chris@87: def __rsub__(self, other): Chris@87: try: Chris@87: coef = self._sub(other, self.coef) Chris@87: except: Chris@87: return NotImplemented Chris@87: return self.__class__(coef, self.domain, self.window) Chris@87: Chris@87: def __rmul__(self, other): Chris@87: try: Chris@87: coef = self._mul(other, self.coef) Chris@87: except: Chris@87: return NotImplemented Chris@87: return self.__class__(coef, self.domain, self.window) Chris@87: Chris@87: def __rdiv__(self, other): Chris@87: # set to __floordiv__ /. Chris@87: return self.__rfloordiv__(other) Chris@87: Chris@87: def __rtruediv__(self, other): Chris@87: # An instance of ABCPolyBase is not considered a Chris@87: # Number. Chris@87: return NotImplemented Chris@87: Chris@87: def __rfloordiv__(self, other): Chris@87: res = self.__rdivmod__(other) Chris@87: if res is NotImplemented: Chris@87: return res Chris@87: return res[0] Chris@87: Chris@87: def __rmod__(self, other): Chris@87: res = self.__rdivmod__(other) Chris@87: if res is NotImplemented: Chris@87: return res Chris@87: return res[1] Chris@87: Chris@87: def __rdivmod__(self, other): Chris@87: try: Chris@87: quo, rem = self._div(other, self.coef) Chris@87: except ZeroDivisionError as e: Chris@87: raise e Chris@87: except: Chris@87: return NotImplemented Chris@87: quo = self.__class__(quo, self.domain, self.window) Chris@87: rem = self.__class__(rem, self.domain, self.window) Chris@87: return quo, rem Chris@87: Chris@87: # Enhance me Chris@87: # some augmented arithmetic operations could be added here Chris@87: Chris@87: def __eq__(self, other): Chris@87: res = (isinstance(other, self.__class__) and Chris@87: np.all(self.domain == other.domain) and Chris@87: np.all(self.window == other.window) and Chris@87: (self.coef.shape == other.coef.shape) and Chris@87: np.all(self.coef == other.coef)) Chris@87: return res Chris@87: Chris@87: def __ne__(self, other): Chris@87: return not self.__eq__(other) Chris@87: Chris@87: # Chris@87: # Extra methods. Chris@87: # Chris@87: Chris@87: def copy(self): Chris@87: """Return a copy. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: new_series : series Chris@87: Copy of self. Chris@87: Chris@87: """ Chris@87: return self.__class__(self.coef, self.domain, self.window) Chris@87: Chris@87: def degree(self): Chris@87: """The degree of the series. Chris@87: Chris@87: .. versionadded:: 1.5.0 Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: degree : int Chris@87: Degree of the series, one less than the number of coefficients. Chris@87: Chris@87: """ Chris@87: return len(self) - 1 Chris@87: Chris@87: def cutdeg(self, deg): Chris@87: """Truncate series to the given degree. Chris@87: Chris@87: Reduce the degree of the series to `deg` by discarding the Chris@87: high order terms. If `deg` is greater than the current degree a Chris@87: copy of the current series is returned. This can be useful in least Chris@87: squares where the coefficients of the high degree terms may be very Chris@87: small. Chris@87: Chris@87: .. versionadded:: 1.5.0 Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: deg : non-negative int Chris@87: The series is reduced to degree `deg` by discarding the high Chris@87: order terms. The value of `deg` must be a non-negative integer. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: new_series : series Chris@87: New instance of series with reduced degree. Chris@87: Chris@87: """ Chris@87: return self.truncate(deg + 1) Chris@87: Chris@87: def trim(self, tol=0): Chris@87: """Remove trailing coefficients Chris@87: Chris@87: Remove trailing coefficients until a coefficient is reached whose Chris@87: absolute value greater than `tol` or the beginning of the series is Chris@87: reached. If all the coefficients would be removed the series is set Chris@87: to ``[0]``. A new series instance is returned with the new Chris@87: coefficients. The current instance remains unchanged. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: tol : non-negative number. Chris@87: All trailing coefficients less than `tol` will be removed. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: new_series : series Chris@87: Contains the new set of coefficients. Chris@87: Chris@87: """ Chris@87: coef = pu.trimcoef(self.coef, tol) Chris@87: return self.__class__(coef, self.domain, self.window) Chris@87: Chris@87: def truncate(self, size): Chris@87: """Truncate series to length `size`. Chris@87: Chris@87: Reduce the series to length `size` by discarding the high Chris@87: degree terms. The value of `size` must be a positive integer. This Chris@87: can be useful in least squares where the coefficients of the Chris@87: high degree terms may be very small. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: size : positive int Chris@87: The series is reduced to length `size` by discarding the high Chris@87: degree terms. The value of `size` must be a positive integer. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: new_series : series Chris@87: New instance of series with truncated coefficients. Chris@87: Chris@87: """ Chris@87: isize = int(size) Chris@87: if isize != size or isize < 1: Chris@87: raise ValueError("size must be a positive integer") Chris@87: if isize >= len(self.coef): Chris@87: coef = self.coef Chris@87: else: Chris@87: coef = self.coef[:isize] Chris@87: return self.__class__(coef, self.domain, self.window) Chris@87: Chris@87: def convert(self, domain=None, kind=None, window=None): Chris@87: """Convert series to a different kind and/or domain and/or window. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: domain : array_like, optional Chris@87: The domain of the converted series. If the value is None, Chris@87: the default domain of `kind` is used. Chris@87: kind : class, optional Chris@87: The polynomial series type class to which the current instance Chris@87: should be converted. If kind is None, then the class of the Chris@87: current instance is used. Chris@87: window : array_like, optional Chris@87: The window of the converted series. If the value is None, Chris@87: the default window of `kind` is used. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: new_series : series Chris@87: The returned class can be of different type than the current Chris@87: instance and/or have a different domain and/or different Chris@87: window. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: Conversion between domains and class types can result in Chris@87: numerically ill defined series. Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: Chris@87: """ Chris@87: if kind is None: Chris@87: kind = self.__class__ Chris@87: if domain is None: Chris@87: domain = kind.domain Chris@87: if window is None: Chris@87: window = kind.window Chris@87: return self(kind.identity(domain, window=window)) Chris@87: Chris@87: def mapparms(self): Chris@87: """Return the mapping parameters. Chris@87: Chris@87: The returned values define a linear map ``off + scl*x`` that is Chris@87: applied to the input arguments before the series is evaluated. The Chris@87: map depends on the ``domain`` and ``window``; if the current Chris@87: ``domain`` is equal to the ``window`` the resulting map is the Chris@87: identity. If the coefficients of the series instance are to be Chris@87: used by themselves outside this class, then the linear function Chris@87: must be substituted for the ``x`` in the standard representation of Chris@87: the base polynomials. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: off, scl : float or complex Chris@87: The mapping function is defined by ``off + scl*x``. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: If the current domain is the interval ``[l1, r1]`` and the window Chris@87: is ``[l2, r2]``, then the linear mapping function ``L`` is Chris@87: defined by the equations:: Chris@87: Chris@87: L(l1) = l2 Chris@87: L(r1) = r2 Chris@87: Chris@87: """ Chris@87: return pu.mapparms(self.domain, self.window) Chris@87: Chris@87: def integ(self, m=1, k=[], lbnd=None): Chris@87: """Integrate. Chris@87: Chris@87: Return a series instance that is the definite integral of the Chris@87: current series. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: m : non-negative int Chris@87: The number of integrations to perform. Chris@87: k : array_like Chris@87: Integration constants. The first constant is applied to the Chris@87: first integration, the second to the second, and so on. The Chris@87: list of values must less than or equal to `m` in length and any Chris@87: missing values are set to zero. Chris@87: lbnd : Scalar Chris@87: The lower bound of the definite integral. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: new_series : series Chris@87: A new series representing the integral. The domain is the same Chris@87: as the domain of the integrated series. Chris@87: Chris@87: """ Chris@87: off, scl = self.mapparms() Chris@87: if lbnd is None: Chris@87: lbnd = 0 Chris@87: else: Chris@87: lbnd = off + scl*lbnd Chris@87: coef = self._int(self.coef, m, k, lbnd, 1./scl) Chris@87: return self.__class__(coef, self.domain, self.window) Chris@87: Chris@87: def deriv(self, m=1): Chris@87: """Differentiate. Chris@87: Chris@87: Return a series instance of that is the derivative of the current Chris@87: series. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: m : non-negative int Chris@87: The number of integrations to perform. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: new_series : series Chris@87: A new series representing the derivative. The domain is the same Chris@87: as the domain of the differentiated series. Chris@87: Chris@87: """ Chris@87: off, scl = self.mapparms() Chris@87: coef = self._der(self.coef, m, scl) Chris@87: return self.__class__(coef, self.domain, self.window) Chris@87: Chris@87: def roots(self): Chris@87: """Return the roots of the series polynomial. Chris@87: Chris@87: Compute the roots for the series. Note that the accuracy of the Chris@87: roots decrease the further outside the domain they lie. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: roots : ndarray Chris@87: Array containing the roots of the series. Chris@87: Chris@87: """ Chris@87: roots = self._roots(self.coef) Chris@87: return pu.mapdomain(roots, self.window, self.domain) Chris@87: Chris@87: def linspace(self, n=100, domain=None): Chris@87: """Return x, y values at equally spaced points in domain. Chris@87: Chris@87: Returns the x, y values at `n` linearly spaced points across the Chris@87: domain. Here y is the value of the polynomial at the points x. By Chris@87: default the domain is the same as that of the series instance. Chris@87: This method is intended mostly as a plotting aid. Chris@87: Chris@87: .. versionadded:: 1.5.0 Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: n : int, optional Chris@87: Number of point pairs to return. The default value is 100. Chris@87: domain : {None, array_like}, optional Chris@87: If not None, the specified domain is used instead of that of Chris@87: the calling instance. It should be of the form ``[beg,end]``. Chris@87: The default is None which case the class domain is used. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: x, y : ndarray Chris@87: x is equal to linspace(self.domain[0], self.domain[1], n) and Chris@87: y is the series evaluated at element of x. Chris@87: Chris@87: """ Chris@87: if domain is None: Chris@87: domain = self.domain Chris@87: x = np.linspace(domain[0], domain[1], n) Chris@87: y = self(x) Chris@87: return x, y Chris@87: Chris@87: @classmethod Chris@87: def fit(cls, x, y, deg, domain=None, rcond=None, full=False, w=None, Chris@87: window=None): Chris@87: """Least squares fit to data. Chris@87: Chris@87: Return a series instance that is the least squares fit to the data Chris@87: `y` sampled at `x`. The domain of the returned instance can be Chris@87: specified and this will often result in a superior fit with less Chris@87: chance of ill conditioning. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: x : array_like, shape (M,) Chris@87: x-coordinates of the M sample points ``(x[i], y[i])``. Chris@87: y : array_like, shape (M,) or (M, K) Chris@87: y-coordinates of the sample points. Several data sets of sample Chris@87: points sharing the same x-coordinates can be fitted at once by Chris@87: passing in a 2D-array that contains one dataset per column. Chris@87: deg : int Chris@87: Degree of the fitting polynomial. Chris@87: domain : {None, [beg, end], []}, optional Chris@87: Domain to use for the returned series. If ``None``, Chris@87: then a minimal domain that covers the points `x` is chosen. If Chris@87: ``[]`` the class domain is used. The default value was the Chris@87: class domain in NumPy 1.4 and ``None`` in later versions. Chris@87: The ``[]`` option was added in numpy 1.5.0. Chris@87: rcond : float, optional Chris@87: Relative condition number of the fit. Singular values smaller Chris@87: than this relative to the largest singular value will be Chris@87: ignored. The default value is len(x)*eps, where eps is the Chris@87: relative precision of the float type, about 2e-16 in most Chris@87: cases. Chris@87: full : bool, optional Chris@87: Switch determining nature of return value. When it is False Chris@87: (the default) just the coefficients are returned, when True Chris@87: diagnostic information from the singular value decomposition is Chris@87: also returned. Chris@87: w : array_like, shape (M,), optional Chris@87: Weights. If not None the contribution of each point Chris@87: ``(x[i],y[i])`` to the fit is weighted by `w[i]`. Ideally the Chris@87: weights are chosen so that the errors of the products Chris@87: ``w[i]*y[i]`` all have the same variance. The default value is Chris@87: None. Chris@87: Chris@87: .. versionadded:: 1.5.0 Chris@87: window : {[beg, end]}, optional Chris@87: Window to use for the returned series. The default Chris@87: value is the default class domain Chris@87: Chris@87: .. versionadded:: 1.6.0 Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: new_series : series Chris@87: A series that represents the least squares fit to the data and Chris@87: has the domain specified in the call. Chris@87: Chris@87: [resid, rank, sv, rcond] : list Chris@87: These values are only returned if `full` = True Chris@87: Chris@87: resid -- sum of squared residuals of the least squares fit Chris@87: rank -- the numerical rank of the scaled Vandermonde matrix Chris@87: sv -- singular values of the scaled Vandermonde matrix Chris@87: rcond -- value of `rcond`. Chris@87: Chris@87: For more details, see `linalg.lstsq`. Chris@87: Chris@87: """ Chris@87: if domain is None: Chris@87: domain = pu.getdomain(x) Chris@87: elif type(domain) is list and len(domain) == 0: Chris@87: domain = cls.domain Chris@87: Chris@87: if window is None: Chris@87: window = cls.window Chris@87: Chris@87: xnew = pu.mapdomain(x, domain, window) Chris@87: res = cls._fit(xnew, y, deg, w=w, rcond=rcond, full=full) Chris@87: if full: Chris@87: [coef, status] = res Chris@87: return cls(coef, domain=domain, window=window), status Chris@87: else: Chris@87: coef = res Chris@87: return cls(coef, domain=domain, window=window) Chris@87: Chris@87: @classmethod Chris@87: def fromroots(cls, roots, domain=[], window=None): Chris@87: """Return series instance that has the specified roots. Chris@87: Chris@87: Returns a series representing the product Chris@87: ``(x - r[0])*(x - r[1])*...*(x - r[n-1])``, where ``r`` is a Chris@87: list of roots. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: roots : array_like Chris@87: List of roots. Chris@87: domain : {[], None, array_like}, optional Chris@87: Domain for the resulting series. If None the domain is the Chris@87: interval from the smallest root to the largest. If [] the Chris@87: domain is the class domain. The default is []. Chris@87: window : {None, array_like}, optional Chris@87: Window for the returned series. If None the class window is Chris@87: used. The default is None. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: new_series : series Chris@87: Series with the specified roots. Chris@87: Chris@87: """ Chris@87: [roots] = pu.as_series([roots], trim=False) Chris@87: if domain is None: Chris@87: domain = pu.getdomain(roots) Chris@87: elif type(domain) is list and len(domain) == 0: Chris@87: domain = cls.domain Chris@87: Chris@87: if window is None: Chris@87: window = cls.window Chris@87: Chris@87: deg = len(roots) Chris@87: off, scl = pu.mapparms(domain, window) Chris@87: rnew = off + scl*roots Chris@87: coef = cls._fromroots(rnew) / scl**deg Chris@87: return cls(coef, domain=domain, window=window) Chris@87: Chris@87: @classmethod Chris@87: def identity(cls, domain=None, window=None): Chris@87: """Identity function. Chris@87: Chris@87: If ``p`` is the returned series, then ``p(x) == x`` for all Chris@87: values of x. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: domain : {None, array_like}, optional Chris@87: If given, the array must be of the form ``[beg, end]``, where Chris@87: ``beg`` and ``end`` are the endpoints of the domain. If None is Chris@87: given then the class domain is used. The default is None. Chris@87: window : {None, array_like}, optional Chris@87: If given, the resulting array must be if the form Chris@87: ``[beg, end]``, where ``beg`` and ``end`` are the endpoints of Chris@87: the window. If None is given then the class window is used. The Chris@87: default is None. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: new_series : series Chris@87: Series of representing the identity. Chris@87: Chris@87: """ Chris@87: if domain is None: Chris@87: domain = cls.domain Chris@87: if window is None: Chris@87: window = cls.window Chris@87: off, scl = pu.mapparms(window, domain) Chris@87: coef = cls._line(off, scl) Chris@87: return cls(coef, domain, window) Chris@87: Chris@87: @classmethod Chris@87: def basis(cls, deg, domain=None, window=None): Chris@87: """Series basis polynomial of degree `deg`. Chris@87: Chris@87: Returns the series representing the basis polynomial of degree `deg`. Chris@87: Chris@87: .. versionadded:: 1.7.0 Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: deg : int Chris@87: Degree of the basis polynomial for the series. Must be >= 0. Chris@87: domain : {None, array_like}, optional Chris@87: If given, the array must be of the form ``[beg, end]``, where Chris@87: ``beg`` and ``end`` are the endpoints of the domain. If None is Chris@87: given then the class domain is used. The default is None. Chris@87: window : {None, array_like}, optional Chris@87: If given, the resulting array must be if the form Chris@87: ``[beg, end]``, where ``beg`` and ``end`` are the endpoints of Chris@87: the window. If None is given then the class window is used. The Chris@87: default is None. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: new_series : series Chris@87: A series with the coefficient of the `deg` term set to one and Chris@87: all others zero. Chris@87: Chris@87: """ Chris@87: if domain is None: Chris@87: domain = cls.domain Chris@87: if window is None: Chris@87: window = cls.window Chris@87: ideg = int(deg) Chris@87: Chris@87: if ideg != deg or ideg < 0: Chris@87: raise ValueError("deg must be non-negative integer") Chris@87: return cls([0]*ideg + [1], domain, window) Chris@87: Chris@87: @classmethod Chris@87: def cast(cls, series, domain=None, window=None): Chris@87: """Convert series to series of this class. Chris@87: Chris@87: The `series` is expected to be an instance of some polynomial Chris@87: series of one of the types supported by by the numpy.polynomial Chris@87: module, but could be some other class that supports the convert Chris@87: method. Chris@87: Chris@87: .. versionadded:: 1.7.0 Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: series : series Chris@87: The series instance to be converted. Chris@87: domain : {None, array_like}, optional Chris@87: If given, the array must be of the form ``[beg, end]``, where Chris@87: ``beg`` and ``end`` are the endpoints of the domain. If None is Chris@87: given then the class domain is used. The default is None. Chris@87: window : {None, array_like}, optional Chris@87: If given, the resulting array must be if the form Chris@87: ``[beg, end]``, where ``beg`` and ``end`` are the endpoints of Chris@87: the window. If None is given then the class window is used. The Chris@87: default is None. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: new_series : series Chris@87: A series of the same kind as the calling class and equal to Chris@87: `series` when evaluated. Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: convert : similar instance method Chris@87: Chris@87: """ Chris@87: if domain is None: Chris@87: domain = cls.domain Chris@87: if window is None: Chris@87: window = cls.window Chris@87: return series.convert(domain, cls, window)