Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/numpy/polynomial/_polybase.py: 23%
414 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1"""
2Abstract base class for the various polynomial Classes.
4The ABCPolyBase class provides the methods needed to implement the common API
5for the various polynomial classes. It operates as a mixin, but uses the
6abc module from the stdlib, hence it is only available for Python >= 2.6.
8"""
9import os
10import abc
11import numbers
13import numpy as np
14from . import polyutils as pu
16__all__ = ['ABCPolyBase']
18class ABCPolyBase(abc.ABC):
19 """An abstract base class for immutable series classes.
21 ABCPolyBase provides the standard Python numerical methods
22 '+', '-', '*', '//', '%', 'divmod', '**', and '()' along with the
23 methods listed below.
25 .. versionadded:: 1.9.0
27 Parameters
28 ----------
29 coef : array_like
30 Series coefficients in order of increasing degree, i.e.,
31 ``(1, 2, 3)`` gives ``1*P_0(x) + 2*P_1(x) + 3*P_2(x)``, where
32 ``P_i`` is the basis polynomials of degree ``i``.
33 domain : (2,) array_like, optional
34 Domain to use. The interval ``[domain[0], domain[1]]`` is mapped
35 to the interval ``[window[0], window[1]]`` by shifting and scaling.
36 The default value is the derived class domain.
37 window : (2,) array_like, optional
38 Window, see domain for its use. The default value is the
39 derived class window.
41 Attributes
42 ----------
43 coef : (N,) ndarray
44 Series coefficients in order of increasing degree.
45 domain : (2,) ndarray
46 Domain that is mapped to window.
47 window : (2,) ndarray
48 Window that domain is mapped to.
50 Class Attributes
51 ----------------
52 maxpower : int
53 Maximum power allowed, i.e., the largest number ``n`` such that
54 ``p(x)**n`` is allowed. This is to limit runaway polynomial size.
55 domain : (2,) ndarray
56 Default domain of the class.
57 window : (2,) ndarray
58 Default window of the class.
60 """
62 # Not hashable
63 __hash__ = None
65 # Opt out of numpy ufuncs and Python ops with ndarray subclasses.
66 __array_ufunc__ = None
68 # Limit runaway size. T_n^m has degree n*m
69 maxpower = 100
71 # Unicode character mappings for improved __str__
72 _superscript_mapping = str.maketrans({
73 "0": "⁰",
74 "1": "¹",
75 "2": "²",
76 "3": "³",
77 "4": "⁴",
78 "5": "⁵",
79 "6": "⁶",
80 "7": "⁷",
81 "8": "⁸",
82 "9": "⁹"
83 })
84 _subscript_mapping = str.maketrans({
85 "0": "₀",
86 "1": "₁",
87 "2": "₂",
88 "3": "₃",
89 "4": "₄",
90 "5": "₅",
91 "6": "₆",
92 "7": "₇",
93 "8": "₈",
94 "9": "₉"
95 })
96 # Some fonts don't support full unicode character ranges necessary for
97 # the full set of superscripts and subscripts, including common/default
98 # fonts in Windows shells/terminals. Therefore, default to ascii-only
99 # printing on windows.
100 _use_unicode = not os.name == 'nt'
102 @property
103 @abc.abstractmethod
104 def domain(self):
105 pass
107 @property
108 @abc.abstractmethod
109 def window(self):
110 pass
112 @property
113 @abc.abstractmethod
114 def basis_name(self):
115 pass
117 @staticmethod
118 @abc.abstractmethod
119 def _add(c1, c2):
120 pass
122 @staticmethod
123 @abc.abstractmethod
124 def _sub(c1, c2):
125 pass
127 @staticmethod
128 @abc.abstractmethod
129 def _mul(c1, c2):
130 pass
132 @staticmethod
133 @abc.abstractmethod
134 def _div(c1, c2):
135 pass
137 @staticmethod
138 @abc.abstractmethod
139 def _pow(c, pow, maxpower=None):
140 pass
142 @staticmethod
143 @abc.abstractmethod
144 def _val(x, c):
145 pass
147 @staticmethod
148 @abc.abstractmethod
149 def _int(c, m, k, lbnd, scl):
150 pass
152 @staticmethod
153 @abc.abstractmethod
154 def _der(c, m, scl):
155 pass
157 @staticmethod
158 @abc.abstractmethod
159 def _fit(x, y, deg, rcond, full):
160 pass
162 @staticmethod
163 @abc.abstractmethod
164 def _line(off, scl):
165 pass
167 @staticmethod
168 @abc.abstractmethod
169 def _roots(c):
170 pass
172 @staticmethod
173 @abc.abstractmethod
174 def _fromroots(r):
175 pass
177 def has_samecoef(self, other):
178 """Check if coefficients match.
180 .. versionadded:: 1.6.0
182 Parameters
183 ----------
184 other : class instance
185 The other class must have the ``coef`` attribute.
187 Returns
188 -------
189 bool : boolean
190 True if the coefficients are the same, False otherwise.
192 """
193 if len(self.coef) != len(other.coef):
194 return False
195 elif not np.all(self.coef == other.coef):
196 return False
197 else:
198 return True
200 def has_samedomain(self, other):
201 """Check if domains match.
203 .. versionadded:: 1.6.0
205 Parameters
206 ----------
207 other : class instance
208 The other class must have the ``domain`` attribute.
210 Returns
211 -------
212 bool : boolean
213 True if the domains are the same, False otherwise.
215 """
216 return np.all(self.domain == other.domain)
218 def has_samewindow(self, other):
219 """Check if windows match.
221 .. versionadded:: 1.6.0
223 Parameters
224 ----------
225 other : class instance
226 The other class must have the ``window`` attribute.
228 Returns
229 -------
230 bool : boolean
231 True if the windows are the same, False otherwise.
233 """
234 return np.all(self.window == other.window)
236 def has_sametype(self, other):
237 """Check if types match.
239 .. versionadded:: 1.7.0
241 Parameters
242 ----------
243 other : object
244 Class instance.
246 Returns
247 -------
248 bool : boolean
249 True if other is same class as self
251 """
252 return isinstance(other, self.__class__)
254 def _get_coefficients(self, other):
255 """Interpret other as polynomial coefficients.
257 The `other` argument is checked to see if it is of the same
258 class as self with identical domain and window. If so,
259 return its coefficients, otherwise return `other`.
261 .. versionadded:: 1.9.0
263 Parameters
264 ----------
265 other : anything
266 Object to be checked.
268 Returns
269 -------
270 coef
271 The coefficients of`other` if it is a compatible instance,
272 of ABCPolyBase, otherwise `other`.
274 Raises
275 ------
276 TypeError
277 When `other` is an incompatible instance of ABCPolyBase.
279 """
280 if isinstance(other, ABCPolyBase):
281 if not isinstance(other, self.__class__):
282 raise TypeError("Polynomial types differ")
283 elif not np.all(self.domain == other.domain):
284 raise TypeError("Domains differ")
285 elif not np.all(self.window == other.window):
286 raise TypeError("Windows differ")
287 return other.coef
288 return other
290 def __init__(self, coef, domain=None, window=None):
291 [coef] = pu.as_series([coef], trim=False)
292 self.coef = coef
294 if domain is not None:
295 [domain] = pu.as_series([domain], trim=False)
296 if len(domain) != 2:
297 raise ValueError("Domain has wrong number of elements.")
298 self.domain = domain
300 if window is not None:
301 [window] = pu.as_series([window], trim=False)
302 if len(window) != 2:
303 raise ValueError("Window has wrong number of elements.")
304 self.window = window
306 def __repr__(self):
307 coef = repr(self.coef)[6:-1]
308 domain = repr(self.domain)[6:-1]
309 window = repr(self.window)[6:-1]
310 name = self.__class__.__name__
311 return f"{name}({coef}, domain={domain}, window={window})"
313 def __format__(self, fmt_str):
314 if fmt_str == '':
315 return self.__str__()
316 if fmt_str not in ('ascii', 'unicode'):
317 raise ValueError(
318 f"Unsupported format string '{fmt_str}' passed to "
319 f"{self.__class__}.__format__. Valid options are "
320 f"'ascii' and 'unicode'"
321 )
322 if fmt_str == 'ascii':
323 return self._generate_string(self._str_term_ascii)
324 return self._generate_string(self._str_term_unicode)
326 def __str__(self):
327 if self._use_unicode:
328 return self._generate_string(self._str_term_unicode)
329 return self._generate_string(self._str_term_ascii)
331 def _generate_string(self, term_method):
332 """
333 Generate the full string representation of the polynomial, using
334 ``term_method`` to generate each polynomial term.
335 """
336 # Get configuration for line breaks
337 linewidth = np.get_printoptions().get('linewidth', 75)
338 if linewidth < 1:
339 linewidth = 1
340 out = f"{self.coef[0]}"
341 for i, coef in enumerate(self.coef[1:]):
342 out += " "
343 power = str(i + 1)
344 # Polynomial coefficient
345 # The coefficient array can be an object array with elements that
346 # will raise a TypeError with >= 0 (e.g. strings or Python
347 # complex). In this case, represent the coefficient as-is.
348 try:
349 if coef >= 0:
350 next_term = f"+ {coef}"
351 else:
352 next_term = f"- {-coef}"
353 except TypeError:
354 next_term = f"+ {coef}"
355 # Polynomial term
356 next_term += term_method(power, "x")
357 # Length of the current line with next term added
358 line_len = len(out.split('\n')[-1]) + len(next_term)
359 # If not the last term in the polynomial, it will be two
360 # characters longer due to the +/- with the next term
361 if i < len(self.coef[1:]) - 1:
362 line_len += 2
363 # Handle linebreaking
364 if line_len >= linewidth:
365 next_term = next_term.replace(" ", "\n", 1)
366 out += next_term
367 return out
369 @classmethod
370 def _str_term_unicode(cls, i, arg_str):
371 """
372 String representation of single polynomial term using unicode
373 characters for superscripts and subscripts.
374 """
375 if cls.basis_name is None:
376 raise NotImplementedError(
377 "Subclasses must define either a basis_name, or override "
378 "_str_term_unicode(cls, i, arg_str)"
379 )
380 return (f"·{cls.basis_name}{i.translate(cls._subscript_mapping)}"
381 f"({arg_str})")
383 @classmethod
384 def _str_term_ascii(cls, i, arg_str):
385 """
386 String representation of a single polynomial term using ** and _ to
387 represent superscripts and subscripts, respectively.
388 """
389 if cls.basis_name is None:
390 raise NotImplementedError(
391 "Subclasses must define either a basis_name, or override "
392 "_str_term_ascii(cls, i, arg_str)"
393 )
394 return f" {cls.basis_name}_{i}({arg_str})"
396 @classmethod
397 def _repr_latex_term(cls, i, arg_str, needs_parens):
398 if cls.basis_name is None:
399 raise NotImplementedError(
400 "Subclasses must define either a basis name, or override "
401 "_repr_latex_term(i, arg_str, needs_parens)")
402 # since we always add parens, we don't care if the expression needs them
403 return f"{{{cls.basis_name}}}_{{{i}}}({arg_str})"
405 @staticmethod
406 def _repr_latex_scalar(x):
407 # TODO: we're stuck with disabling math formatting until we handle
408 # exponents in this function
409 return r'\text{{{}}}'.format(x)
411 def _repr_latex_(self):
412 # get the scaled argument string to the basis functions
413 off, scale = self.mapparms()
414 if off == 0 and scale == 1:
415 term = 'x'
416 needs_parens = False
417 elif scale == 1:
418 term = f"{self._repr_latex_scalar(off)} + x"
419 needs_parens = True
420 elif off == 0:
421 term = f"{self._repr_latex_scalar(scale)}x"
422 needs_parens = True
423 else:
424 term = (
425 f"{self._repr_latex_scalar(off)} + "
426 f"{self._repr_latex_scalar(scale)}x"
427 )
428 needs_parens = True
430 mute = r"\color{{LightGray}}{{{}}}".format
432 parts = []
433 for i, c in enumerate(self.coef):
434 # prevent duplication of + and - signs
435 if i == 0:
436 coef_str = f"{self._repr_latex_scalar(c)}"
437 elif not isinstance(c, numbers.Real):
438 coef_str = f" + ({self._repr_latex_scalar(c)})"
439 elif not np.signbit(c):
440 coef_str = f" + {self._repr_latex_scalar(c)}"
441 else:
442 coef_str = f" - {self._repr_latex_scalar(-c)}"
444 # produce the string for the term
445 term_str = self._repr_latex_term(i, term, needs_parens)
446 if term_str == '1':
447 part = coef_str
448 else:
449 part = rf"{coef_str}\,{term_str}"
451 if c == 0:
452 part = mute(part)
454 parts.append(part)
456 if parts:
457 body = ''.join(parts)
458 else:
459 # in case somehow there are no coefficients at all
460 body = '0'
462 return rf"$x \mapsto {body}$"
466 # Pickle and copy
468 def __getstate__(self):
469 ret = self.__dict__.copy()
470 ret['coef'] = self.coef.copy()
471 ret['domain'] = self.domain.copy()
472 ret['window'] = self.window.copy()
473 return ret
475 def __setstate__(self, dict):
476 self.__dict__ = dict
478 # Call
480 def __call__(self, arg):
481 off, scl = pu.mapparms(self.domain, self.window)
482 arg = off + scl*arg
483 return self._val(arg, self.coef)
485 def __iter__(self):
486 return iter(self.coef)
488 def __len__(self):
489 return len(self.coef)
491 # Numeric properties.
493 def __neg__(self):
494 return self.__class__(-self.coef, self.domain, self.window)
496 def __pos__(self):
497 return self
499 def __add__(self, other):
500 othercoef = self._get_coefficients(other)
501 try:
502 coef = self._add(self.coef, othercoef)
503 except Exception:
504 return NotImplemented
505 return self.__class__(coef, self.domain, self.window)
507 def __sub__(self, other):
508 othercoef = self._get_coefficients(other)
509 try:
510 coef = self._sub(self.coef, othercoef)
511 except Exception:
512 return NotImplemented
513 return self.__class__(coef, self.domain, self.window)
515 def __mul__(self, other):
516 othercoef = self._get_coefficients(other)
517 try:
518 coef = self._mul(self.coef, othercoef)
519 except Exception:
520 return NotImplemented
521 return self.__class__(coef, self.domain, self.window)
523 def __truediv__(self, other):
524 # there is no true divide if the rhs is not a Number, although it
525 # could return the first n elements of an infinite series.
526 # It is hard to see where n would come from, though.
527 if not isinstance(other, numbers.Number) or isinstance(other, bool):
528 raise TypeError(
529 f"unsupported types for true division: "
530 f"'{type(self)}', '{type(other)}'"
531 )
532 return self.__floordiv__(other)
534 def __floordiv__(self, other):
535 res = self.__divmod__(other)
536 if res is NotImplemented:
537 return res
538 return res[0]
540 def __mod__(self, other):
541 res = self.__divmod__(other)
542 if res is NotImplemented:
543 return res
544 return res[1]
546 def __divmod__(self, other):
547 othercoef = self._get_coefficients(other)
548 try:
549 quo, rem = self._div(self.coef, othercoef)
550 except ZeroDivisionError:
551 raise
552 except Exception:
553 return NotImplemented
554 quo = self.__class__(quo, self.domain, self.window)
555 rem = self.__class__(rem, self.domain, self.window)
556 return quo, rem
558 def __pow__(self, other):
559 coef = self._pow(self.coef, other, maxpower=self.maxpower)
560 res = self.__class__(coef, self.domain, self.window)
561 return res
563 def __radd__(self, other):
564 try:
565 coef = self._add(other, self.coef)
566 except Exception:
567 return NotImplemented
568 return self.__class__(coef, self.domain, self.window)
570 def __rsub__(self, other):
571 try:
572 coef = self._sub(other, self.coef)
573 except Exception:
574 return NotImplemented
575 return self.__class__(coef, self.domain, self.window)
577 def __rmul__(self, other):
578 try:
579 coef = self._mul(other, self.coef)
580 except Exception:
581 return NotImplemented
582 return self.__class__(coef, self.domain, self.window)
584 def __rdiv__(self, other):
585 # set to __floordiv__ /.
586 return self.__rfloordiv__(other)
588 def __rtruediv__(self, other):
589 # An instance of ABCPolyBase is not considered a
590 # Number.
591 return NotImplemented
593 def __rfloordiv__(self, other):
594 res = self.__rdivmod__(other)
595 if res is NotImplemented:
596 return res
597 return res[0]
599 def __rmod__(self, other):
600 res = self.__rdivmod__(other)
601 if res is NotImplemented:
602 return res
603 return res[1]
605 def __rdivmod__(self, other):
606 try:
607 quo, rem = self._div(other, self.coef)
608 except ZeroDivisionError:
609 raise
610 except Exception:
611 return NotImplemented
612 quo = self.__class__(quo, self.domain, self.window)
613 rem = self.__class__(rem, self.domain, self.window)
614 return quo, rem
616 def __eq__(self, other):
617 res = (isinstance(other, self.__class__) and
618 np.all(self.domain == other.domain) and
619 np.all(self.window == other.window) and
620 (self.coef.shape == other.coef.shape) and
621 np.all(self.coef == other.coef))
622 return res
624 def __ne__(self, other):
625 return not self.__eq__(other)
627 #
628 # Extra methods.
629 #
631 def copy(self):
632 """Return a copy.
634 Returns
635 -------
636 new_series : series
637 Copy of self.
639 """
640 return self.__class__(self.coef, self.domain, self.window)
642 def degree(self):
643 """The degree of the series.
645 .. versionadded:: 1.5.0
647 Returns
648 -------
649 degree : int
650 Degree of the series, one less than the number of coefficients.
652 """
653 return len(self) - 1
655 def cutdeg(self, deg):
656 """Truncate series to the given degree.
658 Reduce the degree of the series to `deg` by discarding the
659 high order terms. If `deg` is greater than the current degree a
660 copy of the current series is returned. This can be useful in least
661 squares where the coefficients of the high degree terms may be very
662 small.
664 .. versionadded:: 1.5.0
666 Parameters
667 ----------
668 deg : non-negative int
669 The series is reduced to degree `deg` by discarding the high
670 order terms. The value of `deg` must be a non-negative integer.
672 Returns
673 -------
674 new_series : series
675 New instance of series with reduced degree.
677 """
678 return self.truncate(deg + 1)
680 def trim(self, tol=0):
681 """Remove trailing coefficients
683 Remove trailing coefficients until a coefficient is reached whose
684 absolute value greater than `tol` or the beginning of the series is
685 reached. If all the coefficients would be removed the series is set
686 to ``[0]``. A new series instance is returned with the new
687 coefficients. The current instance remains unchanged.
689 Parameters
690 ----------
691 tol : non-negative number.
692 All trailing coefficients less than `tol` will be removed.
694 Returns
695 -------
696 new_series : series
697 New instance of series with trimmed coefficients.
699 """
700 coef = pu.trimcoef(self.coef, tol)
701 return self.__class__(coef, self.domain, self.window)
703 def truncate(self, size):
704 """Truncate series to length `size`.
706 Reduce the series to length `size` by discarding the high
707 degree terms. The value of `size` must be a positive integer. This
708 can be useful in least squares where the coefficients of the
709 high degree terms may be very small.
711 Parameters
712 ----------
713 size : positive int
714 The series is reduced to length `size` by discarding the high
715 degree terms. The value of `size` must be a positive integer.
717 Returns
718 -------
719 new_series : series
720 New instance of series with truncated coefficients.
722 """
723 isize = int(size)
724 if isize != size or isize < 1:
725 raise ValueError("size must be a positive integer")
726 if isize >= len(self.coef):
727 coef = self.coef
728 else:
729 coef = self.coef[:isize]
730 return self.__class__(coef, self.domain, self.window)
732 def convert(self, domain=None, kind=None, window=None):
733 """Convert series to a different kind and/or domain and/or window.
735 Parameters
736 ----------
737 domain : array_like, optional
738 The domain of the converted series. If the value is None,
739 the default domain of `kind` is used.
740 kind : class, optional
741 The polynomial series type class to which the current instance
742 should be converted. If kind is None, then the class of the
743 current instance is used.
744 window : array_like, optional
745 The window of the converted series. If the value is None,
746 the default window of `kind` is used.
748 Returns
749 -------
750 new_series : series
751 The returned class can be of different type than the current
752 instance and/or have a different domain and/or different
753 window.
755 Notes
756 -----
757 Conversion between domains and class types can result in
758 numerically ill defined series.
760 """
761 if kind is None:
762 kind = self.__class__
763 if domain is None:
764 domain = kind.domain
765 if window is None:
766 window = kind.window
767 return self(kind.identity(domain, window=window))
769 def mapparms(self):
770 """Return the mapping parameters.
772 The returned values define a linear map ``off + scl*x`` that is
773 applied to the input arguments before the series is evaluated. The
774 map depends on the ``domain`` and ``window``; if the current
775 ``domain`` is equal to the ``window`` the resulting map is the
776 identity. If the coefficients of the series instance are to be
777 used by themselves outside this class, then the linear function
778 must be substituted for the ``x`` in the standard representation of
779 the base polynomials.
781 Returns
782 -------
783 off, scl : float or complex
784 The mapping function is defined by ``off + scl*x``.
786 Notes
787 -----
788 If the current domain is the interval ``[l1, r1]`` and the window
789 is ``[l2, r2]``, then the linear mapping function ``L`` is
790 defined by the equations::
792 L(l1) = l2
793 L(r1) = r2
795 """
796 return pu.mapparms(self.domain, self.window)
798 def integ(self, m=1, k=[], lbnd=None):
799 """Integrate.
801 Return a series instance that is the definite integral of the
802 current series.
804 Parameters
805 ----------
806 m : non-negative int
807 The number of integrations to perform.
808 k : array_like
809 Integration constants. The first constant is applied to the
810 first integration, the second to the second, and so on. The
811 list of values must less than or equal to `m` in length and any
812 missing values are set to zero.
813 lbnd : Scalar
814 The lower bound of the definite integral.
816 Returns
817 -------
818 new_series : series
819 A new series representing the integral. The domain is the same
820 as the domain of the integrated series.
822 """
823 off, scl = self.mapparms()
824 if lbnd is None:
825 lbnd = 0
826 else:
827 lbnd = off + scl*lbnd
828 coef = self._int(self.coef, m, k, lbnd, 1./scl)
829 return self.__class__(coef, self.domain, self.window)
831 def deriv(self, m=1):
832 """Differentiate.
834 Return a series instance of that is the derivative of the current
835 series.
837 Parameters
838 ----------
839 m : non-negative int
840 Find the derivative of order `m`.
842 Returns
843 -------
844 new_series : series
845 A new series representing the derivative. The domain is the same
846 as the domain of the differentiated series.
848 """
849 off, scl = self.mapparms()
850 coef = self._der(self.coef, m, scl)
851 return self.__class__(coef, self.domain, self.window)
853 def roots(self):
854 """Return the roots of the series polynomial.
856 Compute the roots for the series. Note that the accuracy of the
857 roots decrease the further outside the domain they lie.
859 Returns
860 -------
861 roots : ndarray
862 Array containing the roots of the series.
864 """
865 roots = self._roots(self.coef)
866 return pu.mapdomain(roots, self.window, self.domain)
868 def linspace(self, n=100, domain=None):
869 """Return x, y values at equally spaced points in domain.
871 Returns the x, y values at `n` linearly spaced points across the
872 domain. Here y is the value of the polynomial at the points x. By
873 default the domain is the same as that of the series instance.
874 This method is intended mostly as a plotting aid.
876 .. versionadded:: 1.5.0
878 Parameters
879 ----------
880 n : int, optional
881 Number of point pairs to return. The default value is 100.
882 domain : {None, array_like}, optional
883 If not None, the specified domain is used instead of that of
884 the calling instance. It should be of the form ``[beg,end]``.
885 The default is None which case the class domain is used.
887 Returns
888 -------
889 x, y : ndarray
890 x is equal to linspace(self.domain[0], self.domain[1], n) and
891 y is the series evaluated at element of x.
893 """
894 if domain is None:
895 domain = self.domain
896 x = np.linspace(domain[0], domain[1], n)
897 y = self(x)
898 return x, y
900 @classmethod
901 def fit(cls, x, y, deg, domain=None, rcond=None, full=False, w=None,
902 window=None):
903 """Least squares fit to data.
905 Return a series instance that is the least squares fit to the data
906 `y` sampled at `x`. The domain of the returned instance can be
907 specified and this will often result in a superior fit with less
908 chance of ill conditioning.
910 Parameters
911 ----------
912 x : array_like, shape (M,)
913 x-coordinates of the M sample points ``(x[i], y[i])``.
914 y : array_like, shape (M,)
915 y-coordinates of the M sample points ``(x[i], y[i])``.
916 deg : int or 1-D array_like
917 Degree(s) of the fitting polynomials. If `deg` is a single integer
918 all terms up to and including the `deg`'th term are included in the
919 fit. For NumPy versions >= 1.11.0 a list of integers specifying the
920 degrees of the terms to include may be used instead.
921 domain : {None, [beg, end], []}, optional
922 Domain to use for the returned series. If ``None``,
923 then a minimal domain that covers the points `x` is chosen. If
924 ``[]`` the class domain is used. The default value was the
925 class domain in NumPy 1.4 and ``None`` in later versions.
926 The ``[]`` option was added in numpy 1.5.0.
927 rcond : float, optional
928 Relative condition number of the fit. Singular values smaller
929 than this relative to the largest singular value will be
930 ignored. The default value is len(x)*eps, where eps is the
931 relative precision of the float type, about 2e-16 in most
932 cases.
933 full : bool, optional
934 Switch determining nature of return value. When it is False
935 (the default) just the coefficients are returned, when True
936 diagnostic information from the singular value decomposition is
937 also returned.
938 w : array_like, shape (M,), optional
939 Weights. If not None, the weight ``w[i]`` applies to the unsquared
940 residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are
941 chosen so that the errors of the products ``w[i]*y[i]`` all have
942 the same variance. When using inverse-variance weighting, use
943 ``w[i] = 1/sigma(y[i])``. The default value is None.
945 .. versionadded:: 1.5.0
946 window : {[beg, end]}, optional
947 Window to use for the returned series. The default
948 value is the default class domain
950 .. versionadded:: 1.6.0
952 Returns
953 -------
954 new_series : series
955 A series that represents the least squares fit to the data and
956 has the domain and window specified in the call. If the
957 coefficients for the unscaled and unshifted basis polynomials are
958 of interest, do ``new_series.convert().coef``.
960 [resid, rank, sv, rcond] : list
961 These values are only returned if ``full == True``
963 - resid -- sum of squared residuals of the least squares fit
964 - rank -- the numerical rank of the scaled Vandermonde matrix
965 - sv -- singular values of the scaled Vandermonde matrix
966 - rcond -- value of `rcond`.
968 For more details, see `linalg.lstsq`.
970 """
971 if domain is None:
972 domain = pu.getdomain(x)
973 elif type(domain) is list and len(domain) == 0:
974 domain = cls.domain
976 if window is None:
977 window = cls.window
979 xnew = pu.mapdomain(x, domain, window)
980 res = cls._fit(xnew, y, deg, w=w, rcond=rcond, full=full)
981 if full:
982 [coef, status] = res
983 return cls(coef, domain=domain, window=window), status
984 else:
985 coef = res
986 return cls(coef, domain=domain, window=window)
988 @classmethod
989 def fromroots(cls, roots, domain=[], window=None):
990 """Return series instance that has the specified roots.
992 Returns a series representing the product
993 ``(x - r[0])*(x - r[1])*...*(x - r[n-1])``, where ``r`` is a
994 list of roots.
996 Parameters
997 ----------
998 roots : array_like
999 List of roots.
1000 domain : {[], None, array_like}, optional
1001 Domain for the resulting series. If None the domain is the
1002 interval from the smallest root to the largest. If [] the
1003 domain is the class domain. The default is [].
1004 window : {None, array_like}, optional
1005 Window for the returned series. If None the class window is
1006 used. The default is None.
1008 Returns
1009 -------
1010 new_series : series
1011 Series with the specified roots.
1013 """
1014 [roots] = pu.as_series([roots], trim=False)
1015 if domain is None:
1016 domain = pu.getdomain(roots)
1017 elif type(domain) is list and len(domain) == 0:
1018 domain = cls.domain
1020 if window is None:
1021 window = cls.window
1023 deg = len(roots)
1024 off, scl = pu.mapparms(domain, window)
1025 rnew = off + scl*roots
1026 coef = cls._fromroots(rnew) / scl**deg
1027 return cls(coef, domain=domain, window=window)
1029 @classmethod
1030 def identity(cls, domain=None, window=None):
1031 """Identity function.
1033 If ``p`` is the returned series, then ``p(x) == x`` for all
1034 values of x.
1036 Parameters
1037 ----------
1038 domain : {None, array_like}, optional
1039 If given, the array must be of the form ``[beg, end]``, where
1040 ``beg`` and ``end`` are the endpoints of the domain. If None is
1041 given then the class domain is used. The default is None.
1042 window : {None, array_like}, optional
1043 If given, the resulting array must be if the form
1044 ``[beg, end]``, where ``beg`` and ``end`` are the endpoints of
1045 the window. If None is given then the class window is used. The
1046 default is None.
1048 Returns
1049 -------
1050 new_series : series
1051 Series of representing the identity.
1053 """
1054 if domain is None:
1055 domain = cls.domain
1056 if window is None:
1057 window = cls.window
1058 off, scl = pu.mapparms(window, domain)
1059 coef = cls._line(off, scl)
1060 return cls(coef, domain, window)
1062 @classmethod
1063 def basis(cls, deg, domain=None, window=None):
1064 """Series basis polynomial of degree `deg`.
1066 Returns the series representing the basis polynomial of degree `deg`.
1068 .. versionadded:: 1.7.0
1070 Parameters
1071 ----------
1072 deg : int
1073 Degree of the basis polynomial for the series. Must be >= 0.
1074 domain : {None, array_like}, optional
1075 If given, the array must be of the form ``[beg, end]``, where
1076 ``beg`` and ``end`` are the endpoints of the domain. If None is
1077 given then the class domain is used. The default is None.
1078 window : {None, array_like}, optional
1079 If given, the resulting array must be if the form
1080 ``[beg, end]``, where ``beg`` and ``end`` are the endpoints of
1081 the window. If None is given then the class window is used. The
1082 default is None.
1084 Returns
1085 -------
1086 new_series : series
1087 A series with the coefficient of the `deg` term set to one and
1088 all others zero.
1090 """
1091 if domain is None:
1092 domain = cls.domain
1093 if window is None:
1094 window = cls.window
1095 ideg = int(deg)
1097 if ideg != deg or ideg < 0:
1098 raise ValueError("deg must be non-negative integer")
1099 return cls([0]*ideg + [1], domain, window)
1101 @classmethod
1102 def cast(cls, series, domain=None, window=None):
1103 """Convert series to series of this class.
1105 The `series` is expected to be an instance of some polynomial
1106 series of one of the types supported by by the numpy.polynomial
1107 module, but could be some other class that supports the convert
1108 method.
1110 .. versionadded:: 1.7.0
1112 Parameters
1113 ----------
1114 series : series
1115 The series instance to be converted.
1116 domain : {None, array_like}, optional
1117 If given, the array must be of the form ``[beg, end]``, where
1118 ``beg`` and ``end`` are the endpoints of the domain. If None is
1119 given then the class domain is used. The default is None.
1120 window : {None, array_like}, optional
1121 If given, the resulting array must be if the form
1122 ``[beg, end]``, where ``beg`` and ``end`` are the endpoints of
1123 the window. If None is given then the class window is used. The
1124 default is None.
1126 Returns
1127 -------
1128 new_series : series
1129 A series of the same kind as the calling class and equal to
1130 `series` when evaluated.
1132 See Also
1133 --------
1134 convert : similar instance method
1136 """
1137 if domain is None:
1138 domain = cls.domain
1139 if window is None:
1140 window = cls.window
1141 return series.convert(domain, cls, window)