Coverage for pygeodesy/fstats.py: 98%
327 statements
« prev ^ index » next coverage.py v7.2.2, created at 2024-05-15 16:36 -0400
« prev ^ index » next coverage.py v7.2.2, created at 2024-05-15 16:36 -0400
2# -*- coding: utf-8 -*-
4u'''Classes for I{running} statistics and regressions based on
5L{pygeodesy.Fsum}, precision floating point summation.
6'''
7# make sure int/int division yields float quotient, see .basics
8from __future__ import division as _; del _ # PYCHOK semicolon
10from pygeodesy.basics import isscalar, isodd, _xinstanceof, \
11 _xiterable, _xsubclassof, _zip
12from pygeodesy.constants import _0_0, _1_0, _2_0, _3_0, _4_0, _6_0
13from pygeodesy.errors import _AssertionError, _ValueError, _xError
14from pygeodesy.fmath import Fsqrt, Fmt
15from pygeodesy.fsums import _2finite, Fsum, _iadd_op_, _isFsumTuple
16from pygeodesy.interns import NN, _odd_, _SPACE_
17from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY
18from pygeodesy.named import _Named, _NotImplemented, property_RO
19# from pygeodesy.props import property_RO # from .named
20# from pygeodesy.streprs import Fmt # from .fmath
22__all__ = _ALL_LAZY.fstats
23__version__ = '24.05.10'
26def _2Floats(**xs):
27 '''(INTERNAL) Yield each value as C{float} or L{Fsum}.
28 '''
29 try:
30 name, xs = xs.popitem()
31 except Exception as X:
32 raise _AssertionError(xs=xs, cause=X)
34 try:
35 i = None
36 for i, x in enumerate(_xiterable(xs)): # don't unravel Fsums
37 yield x._Fsum if _isFsumTuple(x) else _2finite(x)
38 except Exception as X:
39 raise _xError(X, name, xs) if i is None else \
40 _xError(X, Fmt.INDEX(name, i), x)
43def _sampled(n, sample):
44 '''(INTERNAL) Return the sample or the entire count.
45 '''
46 return (n - 1) if sample and n > 0 else n
49class _FstatsNamed(_Named):
50 '''(INTERNAL) Base class.
51 '''
52 _n = 0
54 def __add__(self, other):
55 '''Sum of this and an other instance or a C{scalar} or an
56 L{Fsum}, L{Fsum2Tuple} or
57 .
58 '''
59 f = self.copy(name=self.__add__.__name__) # PYCHOK expected
60 f += other
61 return f
63 def __float__(self): # PYCHOK no cover
64 '''Not implemented.'''
65 return _NotImplemented(self)
67 def __int__(self): # PYCHOK no cover
68 '''Not implemented.'''
69 return _NotImplemented(self)
71 def __len__(self):
72 '''Return the I{total} number of accumulated C{Scalars} (C{int}).
73 '''
74 return self._n
76 def __neg__(self): # PYCHOK no cover
77 '''Not implemented.'''
78 return _NotImplemented(self)
80 def __radd__(self, other): # PYCHOK no cover
81 '''Not implemented.'''
82 return _NotImplemented(self, other)
84 def __str__(self):
85 n = self.name
86 n = _SPACE_(self.classname, n) if n else self.classname
87 return Fmt.SQUARE(n, len(self))
89 def copy(self, deep=False, name=NN):
90 '''Copy this instance, C{shallow} or B{C{deep}}.
91 '''
92 n = name or self.copy.__name__
93 f = _Named.copy(self, deep=deep, name=n)
94 return self._copy(f, self) # PYCHOK expected
96 fcopy = copy # for backward compatibility
99class _FstatsBase(_FstatsNamed):
100 '''(INTERNAL) Base running stats class.
101 '''
102 _Ms = ()
104 def _copy(self, d, s):
105 '''(INTERNAL) Copy C{B{c} = B{s}}.
106 '''
107 _xinstanceof(self.__class__, d=d, s=s)
108 d._Ms = tuple(M.copy() for M in s._Ms) # deep=False
109 d._n = s._n
110 return d
112 def fadd(self, xs, sample=False): # PYCHOK no cover
113 '''I{Must be overloaded}.'''
114 self._notOverloaded(xs, sample=sample)
116 def fadd_(self, *xs, **sample):
117 '''Accumulate and return the current count.
119 @see: Method C{fadd} for further details.
120 '''
121 return self.fadd(xs, **sample)
123 def fmean(self, xs=None):
124 '''Accumulate and return the current mean.
126 @kwarg xs: Iterable of additional values (each C{scalar} or
127 an L{Fsum} or L{Fsum2Tuple} instance).
129 @return: Current, running mean (C{float}).
131 @see: Method C{fadd}.
132 '''
133 return float(self._Mean(xs))
135 def fmean_(self, *xs):
136 '''Accumulate and return the current mean.
138 @see: Method C{fmean} for further details.
139 '''
140 return self.fmean(xs)
142 def fstdev(self, xs=None, **sample):
143 '''Accumulate and return the current standard deviation.
145 @arg xs: Iterable of additional values (each C{scalar} or an
146 L{Fsum} or L{Fsum2Tuple} instance).
147 @kwarg sample: Use C{B{sample}=True} for the I{sample} deviation
148 instead of the I{population} deviation (C{bool}).
150 @return: Current, running (sample) standard deviation (C{float}).
152 @see: Method C{fadd}.
153 '''
154 return float(self._Stdev(xs, **sample))
156 def fstdev_(self, *xs, **sample):
157 '''Accumulate and return the current standard deviation.
159 @see: Method C{fstdev} for further details.
160 '''
161 return self.fstdev(xs, **sample)
163 def fvariance(self, xs=None, **sample):
164 '''Accumulate and return the current variance.
166 @arg xs: Iterable of additional values (each C{scalar} or an
167 L{Fsum} or L{Fsum2Tuple} instance).
168 @kwarg sample: Use C{B{sample}=True} for the I{sample} variance
169 instead of the I{population} variance (C{bool}).
171 @return: Current, running (sample) variance (C{float}).
173 @see: Method C{fadd}.
174 '''
175 return float(self._Variance(xs, **sample))
177 def fvariance_(self, *xs, **sample):
178 '''Accumulate and return the current variance.
180 @see: Method C{fvariance} for further details.
181 '''
182 return self.fvariance(xs, **sample)
184 def _iadd_other(self, other):
185 '''(INTERNAL) Add one or several values.
186 '''
187 try:
188 if _isFsumTuple(other):
189 self.fadd_(other._Fsum)
190 elif isscalar(other):
191 self.fadd_(_2finite(other))
192 elif _xiterable(other):
193 self.fadd(other)
194 except Exception as X:
195 t = _SPACE_(self, _iadd_op_, repr(other))
196 raise _xError(X, t)
198 @property_RO
199 def _M1(self):
200 '''(INTERNAL) get the 1st Moment accumulator.'''
201 return self._Ms[0]
203 @property_RO
204 def _M2(self):
205 '''(INTERNAL) get the 2nd Moment accumulator.'''
206 return self._Ms[1]
208 def _Mean(self, xs=None):
209 '''(INTERNAL) Return the current mean as L{Fsum}.
210 '''
211 if xs:
212 self.fadd(xs)
213 return self._M1 # .copy()
215 def _Stdev(self, xs=None, **sample):
216 '''(INTERNAL) Return the current (sample) standard deviation as L{Fsum}.
217 '''
218 V = self._Variance(xs, **sample)
219 return Fsqrt(V) if V > 0 else _0_0
221 def _Variance(self, xs=None, **sample):
222 '''(INTERNAL) Return the current (sample) variance as L{Fsum}.
223 '''
224 n = self.fadd(xs, **sample)
225 return (self._M2 / n) if n > 0 else _0_0
228class Fcook(_FstatsBase):
229 '''U{Cook<https://www.JohnDCook.com/blog/skewness_kurtosis>}'s
230 C{RunningStats} computing the running mean, median and
231 (sample) kurtosis, skewness, variance, standard deviation
232 and Jarque-Bera normality.
234 @see: L{Fwelford} and U{Higher-order statistics<https://
235 WikiPedia.org/wiki/Algorithms_for_calculating_variance>}.
236 '''
237 def __init__(self, xs=None, name=NN):
238 '''New L{Fcook} stats accumulator.
240 @arg xs: Iterable of additional values (each C{scalar} or
241 an L{Fsum} or L{Fsum2Tuple} instance).
242 @kwarg name: Optional name (C{str}).
244 @see: Method L{Fcook.fadd}.
245 '''
246 self._Ms = tuple(Fsum() for _ in range(4)) # 1st, 2nd ... Moment
247 if name:
248 self.name = name
249 if xs:
250 self.fadd(xs)
252 def __iadd__(self, other):
253 '''Add B{C{other}} to this L{Fcook} instance.
255 @arg other: An L{Fcook} instance or value or iterable
256 of values (each C{scalar} or an L{Fsum}
257 or L{Fsum2Tuple} instance).
259 @return: This instance, updated (L{Fcook}).
261 @raise TypeError: Invalid B{C{other}}.
263 @raise ValueError: Invalid or non-finite B{C{other}}.
265 @see: Method L{Fcook.fadd}.
266 '''
267 if isinstance(other, Fcook):
268 nb = len(other)
269 if nb > 0:
270 na = len(self)
271 if na > 0:
272 A1, A2, A3, A4 = self._Ms
273 B1, B2, B3, B4 = other._Ms
275 n = na + nb
276 _n = _1_0 / n
277 D = A1 - B1 # b1 - a1
278 Dn = D * _n
279 Dn2 = Dn**2 # d**2 / n**2
280 nab = na * nb
281 Dn3 = Dn2 * (D * nab)
283 na2 = na**2
284 nb2 = nb**2
285 A4 += B4
286 A4 += (B3 * na - (A3 * nb)) * (Dn * _4_0)
287 A4 += (B2 * na2 + (A2 * nb2)) * (Dn2 * _6_0)
288 A4 += (Dn * Dn3) * (na2 - nab + nb2) # d**4 / n**3
290 A3 += B3
291 A3 += (A2 * na - (B2 * nb)) * (Dn * _3_0)
292 A3 += Dn3 * (na - nb)
294 A2 += B2
295 A2 += Dn2 * (nab * _n)
297 B1n = B1 * nb # if other is self
298 A1 *= na
299 A1 += B1n
300 A1 *= _n
302# self._Ms = A1, A2, A3, A4
303 self._n = n
304 else:
305 self._copy(self, other)
306 else:
307 self._iadd_other(other)
308 return self
310 def fadd(self, xs, sample=False):
311 '''Accumulate and return the current count.
313 @arg xs: Iterable of additional values (each C{scalar} or an
314 L{Fsum} or L{Fsum2Tuple} instance).
315 @kwarg sample: Use C{B{sample}=True} for the I{sample} count
316 instead of the I{population} count (C{bool}).
318 @return: Current, running (sample) count (C{int}).
320 @raise OverflowError: Partial C{2sum} overflow.
322 @raise TypeError: Invalid B{C{xs}}.
324 @raise ValueError: Invalid or non-finite B{C{xs}}.
326 @see: U{online_kurtosis<https://WikiPedia.org/wiki/
327 Algorithms_for_calculating_variance>}.
328 '''
329 n = self._n
330 if xs:
331 M1, M2, M3, M4 = self._Ms
332 for x in _2Floats(xs=xs): # PYCHOK yield
333 n1 = n
334 n += 1
335 D = x - M1
336 Dn = D / n
337 if Dn:
338 Dn2 = Dn**2
339 if n1 > 1:
340 T1 = D * (Dn * n1)
341 T2 = T1 * (Dn * (n1 - 1))
342 T3 = T1 * (Dn2 * (n**2 - 3 * n1))
343 elif n1 > 0: # n1 == 1, n == 2
344 T1 = D * Dn
345 T2 = _0_0
346 T3 = T1 * Dn2
347 else:
348 T1 = T2 = T3 = _0_0
349 M4 += T3
350 M4 -= M3 * (Dn * _4_0)
351 M4 += M2 * (Dn2 * _6_0)
353 M3 += T2
354 M3 -= M2 * (Dn * _3_0)
356 M2 += T1
357 M1 += Dn
358# self._Ms = M1, M2, M3, M4
359 self._n = n
360 return _sampled(n, sample)
362 def fjb(self, xs=None, excess=True, sample=True):
363 '''Accumulate and compute the current U{Jarque-Bera
364 <https://WikiPedia.org/wiki/Jarque–Bera_test>} normality.
366 @kwarg xs: Iterable of additional values (each C{scalar} or an
367 L{Fsum} or L{Fsum2Tuple}).
368 @kwarg excess: Apply the I{excess} kurtosis (C{bool}), default.
369 @kwarg sample: Use C{B{sample}=False} for the I{population}
370 normality instead of the I{sample} one (C{bool}).
372 @return: Current, running (sample) Jarque-Bera normality (C{float}).
374 @see: Method L{Fcook.fadd}.
375 '''
376 return float(self._JarqueBera(xs, excess, sample=sample))
378 def fjb_(self, *xs, **sample_excess):
379 '''Accumulate and compute the current U{Jarque-Bera
380 <https://WikiPedia.org/wiki/Jarque–Bera_test>} normality.
382 @see: Method L{Fcook.fjb} for further details.
383 '''
384 return self.fjb(xs, **sample_excess)
386 def fkurtosis(self, xs=None, excess=True, **sample):
387 '''Accumulate and return the current kurtosis.
389 @arg xs: Iterable of additional values (each C{scalar} or an
390 L{Fsum} or L{Fsum2Tuple} instance).
391 @kwarg excess: Return the I{excess} kurtosis (C{bool}), default.
392 @kwarg sample: Use C{B{sample}=True} for the I{sample} kurtosis
393 instead of the I{population} kurtosis (C{bool}).
395 @return: Current, running (sample) kurtosis or I{excess} kurtosis (C{float}).
397 @see: U{Kurtosis Formula<https://www.Macroption.com/kurtosis-formula>}
398 and U{Mantalos<https://www.ResearchGate.net/publication/227440210>}.
400 @see: Method L{Fcook.fadd}.
401 '''
402 n = self.fadd(xs, **sample)
403 return float(self._Kurtosis(n, excess, **sample))
405 def fkurtosis_(self, *xs, **excess_sample):
406 '''Accumulate and return the current kurtosis.
408 @see: Method L{Fcook.fkurtosis} for further details.
409 '''
410 return self.fkurtosis(xs, **excess_sample)
412 def fmedian(self, xs=None):
413 '''Accumulate and return the current median.
415 @arg xs: Iterable of additional values (each C{scalar} or an
416 L{Fsum} or L{Fsum2Tuple} instance).
418 @return: Current, running median (C{float}).
420 @see: U{Pearson's Skewness Coefficients<https://MathWorld.Wolfram.com/
421 PearsonsSkewnessCoefficients.html>}, U{Skewness & Kurtosis Simplified
422 https://TowardsDataScience.com/skewness-kurtosis-simplified-1338e094fc85>}
423 and method L{Fcook.fadd}.
424 '''
425 return float(self._Median(xs))
427 def fmedian_(self, *xs):
428 '''Accumulate and return the current median.
430 @see: Method L{Fcook.fmedian} for further details.
431 '''
432 return self.fmedian(xs)
434 def fskewness(self, xs=None, **sample):
435 '''Accumulate and return the current skewness.
437 @arg xs: Iterable of additional values (each C{scalar} or an
438 L{Fsum} or L{Fsum2Tuple} instance).
439 @kwarg sample: Use C{B{sample}=True} for the I{sample} skewness
440 instead of the I{population} skewness (C{bool}).
442 @return: Current, running (sample) skewness (C{float}).
444 @see: U{Skewness Formula<https://www.Macroption.com/skewness-formula/>}
445 and U{Mantalos<https://www.ResearchGate.net/publication/227440210>}.
447 @see: Method L{Fcook.fadd}.
448 '''
449 n = self.fadd(xs, **sample)
450 return float(self._Skewness(n, **sample))
452 def fskewness_(self, *xs, **sample):
453 '''Accumulate and return the current skewness.
455 @see: Method L{Fcook.fskewness} for further details.
456 '''
457 return self.fskewness(xs, **sample)
459 def _JarqueBera(self, xs, excess, **sample):
460 '''(INTERNAL) Return the (sample) Jarque-Bera normality as L{Fsum}.
461 '''
462 N, n = _0_0, self.fadd(xs, **sample)
463 if n > 0:
464 K = self._Kurtosis(n, excess, **sample) / _2_0
465 S = self._Skewness(n, **sample)
466 N = (K**2 + S**2) * (n / _6_0) # Fpowers(2, K, S) * ...
467 return N
469 def _Kurtosis(self, n, excess, sample=False):
470 '''(INTERNAL) Return the (sample) kurtosis as L{Fsum} or C{0.0}.
471 '''
472 K = _0_0
473 if n > 0:
474 _, M2, _, M4 = self._Ms
475 M = M2**2
476 if M > 0:
477 K, x = M.rdiv(M4 * n, raiser=False), _3_0
478 if sample and 2 < n < len(self):
479 d = (n - 1) * (n - 2)
480 K *= (n + 1) * (n + 2) / d
481 x *= n**2 / d
482 if excess:
483 K -= x
484 return K
486 def _Median(self, xs=None):
487 '''(INTERNAL) Return the median as L{Fsum}.
488 '''
489 # skewness = 3 * (mean - median) / stdev, i.e.
490 # median = mean - (skewness * stdef) / 3
491 return self._Mean(xs) - (self._Skewness(self._n) *
492 self._Stdev()) / _3_0
494 def _Skewness(self, n, sample=False):
495 '''(INTERNAL) Return the (sample) skewness as L{Fsum} or C{0.0}.
496 '''
497 S = _0_0
498 if n > 0:
499 _, M2, M3, _ = self._Ms
500 M = M2**3
501 if M > 0:
502 M = M.rdiv(n, raiser=False)
503 S = M3 * Fsqrt(M, raiser=False)
504 if sample and 1 < n < len(self):
505 S *= (n + 1) / (n - 1)
506 return S
508 def toFwelford(self, name=NN):
509 '''Return an L{Fwelford} equivalent.
510 '''
511 f = Fwelford(name=name or self.name)
512 f._Ms = self._M1.copy(), self._M2.copy() # deep=False
513 f._n = self._n
514 return f
517class Fwelford(_FstatsBase):
518 '''U{Welford<https://WikiPedia.org/wiki/Algorithms_for_calculating_variance>}'s
519 accumulator computing the running mean, (sample) variance and standard deviation.
521 @see: U{Cook<https://www.JohnDCook.com/blog/standard_deviation/>} and L{Fcook}.
522 '''
523 def __init__(self, xs=None, name=NN):
524 '''New L{Fwelford} stats accumulator.
526 @arg xs: Iterable of initial values (each C{scalar} or an
527 L{Fsum} or L{Fsum2Tuple} instance).
528 @kwarg name: Optional name (C{str}).
530 @see: Method L{Fwelford.fadd}.
531 '''
532 self._Ms = Fsum(), Fsum() # 1st and 2nd Moment
533 if name:
534 self.name = name
535 if xs:
536 self.fadd(xs)
538 def __iadd__(self, other):
539 '''Add B{C{other}} to this L{Fwelford} instance.
541 @arg other: An L{Fwelford} or L{Fcook} instance or value
542 or an iterable of values (each C{scalar} or
543 an L{Fsum} or L{Fsum2Tuple} instance).
545 @return: This instance, updated (L{Fwelford}).
547 @raise TypeError: Invalid B{C{other}}.
549 @raise ValueError: Invalid B{C{other}}.
551 @see: Method L{Fwelford.fadd} and U{Parallel algorithm<https//
552 WikiPedia.org/wiki/Algorithms_for_calculating_variance>}.
553 '''
554 if isinstance(other, Fwelford):
555 nb = len(other)
556 if nb > 0:
557 na = len(self)
558 if na > 0:
559 M, S = self._Ms
560 M_, S_ = other._Ms
562 n = na + nb
563 _n = _1_0 / n
565 D = M_ - M
566 D *= D # D**2
567 D *= na * nb * _n
568 S += D
569 S += S_
571 Mn = M_ * nb # if other is self
572 M *= na
573 M += Mn
574 M *= _n
576# self._Ms = M, S
577 self._n = n
578 else:
579 self._copy(self, other)
581 elif isinstance(other, Fcook):
582 self += other.toFwelford()
583 else:
584 self._iadd_other(other)
585 return self
587 def fadd(self, xs, sample=False):
588 '''Accumulate and return the current count.
590 @arg xs: Iterable of additional values (each C{scalar} or an
591 L{Fsum} or L{Fsum2Tuple} instance).
592 @kwarg sample: Use C{B{sample}=True} for the I{sample} count
593 instead of the I{population} count (C{bool}).
595 @return: Current, running (sample) count (C{int}).
597 @raise OverflowError: Partial C{2sum} overflow.
599 @raise TypeError: Invalid B{C{xs}}.
601 @raise ValueError: Invalid or non-finite B{C{xs}}.
602 '''
603 n = self._n
604 if xs:
605 M, S = self._Ms
606 for x in _2Floats(xs=xs): # PYCHOK yield
607 n += 1
608 D = x - M
609 M += D / n
610 D *= x - M
611 S += D
612# self._Ms = M, S
613 self._n = n
614 return _sampled(n, sample)
617class Flinear(_FstatsNamed):
618 '''U{Cook<https://www.JohnDCook.com/blog/running_regression>}'s
619 C{RunningRegression} computing the running slope, intercept
620 and correlation of a linear regression.
621 '''
622 def __init__(self, xs=None, ys=None, Fstats=Fwelford, name=NN):
623 '''New L{Flinear} regression accumulator.
625 @kwarg xs: Iterable of initial C{x} values (each C{scalar} or
626 an L{Fsum} or L{Fsum2Tuple} instance).
627 @kwarg ys: Iterable of initial C{y} values (each C{scalar} or
628 an L{Fsum} or L{Fsum2Tuple} instance).
629 @kwarg Fstats: Class for C{xs} and C{ys} values (L{Fcook} or
630 L{Fwelford}).
631 @kwarg name: Optional name (C{str}).
633 @raise TypeError: B{C{Fstats}} not L{Fcook} or L{Fwelford}.
635 @see: Method L{Flinear.fadd}.
636 '''
637 _xsubclassof(Fcook, Fwelford, Fstats=Fstats)
638 if name:
639 self.name = name
641 self._S = Fsum(name=name)
642 self._X = Fstats(name=name)
643 self._Y = Fstats(name=name)
644 if xs and ys:
645 self.fadd(xs, ys)
647 def __iadd__(self, other):
648 '''Add B{C{other}} to this instance.
650 @arg other: An L{Flinear} instance or an iterable of
651 C{x_ys} values, see method C{fadd_}.
653 @return: This instance, updated (L{Flinear}).
655 @raise TypeError: Invalid B{C{other}} or the B{C{other}}
656 and these C{x} and C{y} accumulators
657 are not compatible.
659 @raise ValueError: Invalid or odd-length B{C{other}}.
661 @see: Method L{Flinear.fadd_}.
662 '''
663 if isinstance(other, Flinear):
664 if len(other) > 0:
665 if len(self) > 0:
666 n = other._n
667 D = (other._X._M1 - self._X._M1) * \
668 (other._Y._M1 - self._Y._M1) * \
669 (n * self._n / (self._n + n))
670 self._S += other._S + D
671 self._X += other._X
672 self._Y += other._Y
673 self._n += n
674 else:
675 self._copy(self, other)
676 else:
677 try:
678 if _xiterable(other):
679 self.fadd_(*other)
680 except Exception as X:
681 op = _SPACE_(self, _iadd_op_, repr(other))
682 raise _xError(X, op)
683 return self
685 def _copy(self, d, s):
686 '''(INTERNAL) Copy C{B{d} = B{s}}.
687 '''
688 _xinstanceof(Flinear, d=d, s=s)
689 d._S = s._S.copy(deep=False)
690 d._X = s._X.copy(deep=False)
691 d._Y = s._Y.copy(deep=False)
692 d._n = s._n
693 return d
695 def _Correlation(self, **sample):
696 '''(INTERNAL) Return the current (sample) correlation as L{Fsum}.
697 '''
698 return self._Sampled(self._X._Stdev(**sample) *
699 self._Y._Stdev(**sample), **sample)
701 def fadd(self, xs, ys, sample=False):
702 '''Accumulate and return the current count.
704 @arg xs: Iterable of additional C{x} values (each C{scalar}
705 or an L{Fsum} or L{Fsum2Tuple} instance).
706 @arg ys: Iterable of additional C{y} values (each C{scalar}
707 or an L{Fsum} or L{Fsum2Tuple} instance).
708 @kwarg sample: Use C{B{sample}=True} for the I{sample} count
709 instead of the I{population} count (C{bool}).
711 @return: Current, running (sample) count (C{int}).
713 @raise OverflowError: Partial C{2sum} overflow.
715 @raise TypeError: Invalid B{C{xs}} or B{C{ys}}.
717 @raise ValueError: Invalid or non-finite B{C{xs}} or B{C{ys}}.
718 '''
719 n = self._n
720 if xs and ys:
721 S = self._S
722 X = self._X
723 Y = self._Y
724 for x, y in _zip(_2Floats(xs=xs), _2Floats(ys=ys)): # PYCHOK strict=True
725 n1 = n
726 n += 1
727 if n1 > 0:
728 S += (X._M1 - x) * (Y._M1 - y) * (n1 / n)
729 X += x
730 Y += y
731 self._n = n
732 return _sampled(n, sample)
734 def fadd_(self, *x_ys, **sample):
735 '''Accumulate and return the current count.
737 @arg x_ys: Individual, alternating C{x, y, x, y, ...} values
738 (each C{scalar} or an L{Fsum} or L{Fsum2Tuple}
739 instance).
741 @see: Method C{Flinear.fadd} for further details.
742 '''
743 if isodd(len(x_ys)):
744 t = _SPACE_(_odd_, len.__name__)
745 raise _ValueError(t, len(x_ys))
746 return self.fadd(x_ys[0::2], x_ys[1::2], **sample)
748 def fcorrelation(self, **sample):
749 '''Return the current, running (sample) correlation (C{float}).
751 @kwarg sample: Use C{B{sample}=True} for the I{sample} correlation
752 instead of the I{population} correlation (C{bool}).
753 '''
754 return float(self._Correlation(**sample))
756 def fintercept(self, **sample):
757 '''Return the current, running (sample) intercept (C{float}).
759 @kwarg sample: Use C{B{sample}=True} for the I{sample} intercept
760 instead of the I{population} intercept (C{bool}).
761 '''
762 return float(self._Intercept(**sample))
764 def fslope(self, **sample):
765 '''Return the current, running (sample) slope (C{float}).
767 @kwarg sample: Use C{B{sample}=True} for the I{sample} slope
768 instead of the I{population} slope (C{bool}).
769 '''
770 return float(self._Slope(**sample))
772 def _Intercept(self, **sample):
773 '''(INTERNAL) Return the current (sample) intercept as L{Fsum}.
774 '''
775 return self._Y._M1 - self._X._M1 * self._Slope(**sample)
777 def _Sampled(self, T, sample=False):
778 '''(INTERNAL) Compute the sampled or entire population result.
779 '''
780 T *= _sampled(self._n, sample)
781 return self._S.copy().fdiv(T, raiser=False) if T else T
783 def _Slope(self, **sample):
784 '''(INTERNAL) Return the current (sample) slope as L{Fsum}.
785 '''
786 return self._Sampled(self._X._Variance(**sample), **sample)
788 @property_RO
789 def x(self):
790 '''Get the C{x} accumulator (L{Fcook} or L{Fwelford}).
791 '''
792 return self._X # .copy()
794 @property_RO
795 def y(self):
796 '''Get the C{y} accumulator (L{Fcook} or L{Fwelford}).
797 '''
798 return self._Y # .copy()
801__all__ += _ALL_DOCS(_FstatsBase, _FstatsNamed)
803# **) MIT License
804#
805# Copyright (C) 2021-2024 -- mrJean1 at Gmail -- All Rights Reserved.
806#
807# Permission is hereby granted, free of charge, to any person obtaining a
808# copy of this software and associated documentation files (the "Software"),
809# to deal in the Software without restriction, including without limitation
810# the rights to use, copy, modify, merge, publish, distribute, sublicense,
811# and/or sell copies of the Software, and to permit persons to whom the
812# Software is furnished to do so, subject to the following conditions:
813#
814# The above copyright notice and this permission notice shall be included
815# in all copies or substantial portions of the Software.
816#
817# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
818# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
819# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
820# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
821# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
822# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
823# OTHER DEALINGS IN THE SOFTWARE.