Coverage for pygeodesy/fstats.py: 98%
327 statements
« prev ^ index » next coverage.py v7.2.2, created at 2024-05-08 14:50 -0400
« prev ^ index » next coverage.py v7.2.2, created at 2024-05-08 14:50 -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
15from pygeodesy.fsums import _2finite, _Float, Fsum, _iadd_op_, \
16 _isAn, _isFsumTuple, _Tuple, Fmt
17from pygeodesy.interns import NN, _odd_, _SPACE_
18from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY
19from pygeodesy.named import _Named, _NotImplemented, property_RO
20# from pygeodesy.props import property_RO # from .named
21# from pygeodesy.streprs import Fmt # from .fsums
23__all__ = _ALL_LAZY.fstats
24__version__ = '24.05.08'
27def _2Floats(**xs):
28 '''(INTERNAL) Yield each value as C{float} or L{Fsum}.
29 '''
30 try:
31 name, xs = xs.popitem()
32 except Exception as X:
33 raise _AssertionError(xs=xs, cause=X)
35 try:
36 i, x = 0, None
37 for i, x in enumerate(xs): # don't unravel Fsums
38 yield x._Fsum if _isFsumTuple(x) else _2finite(x)
39 except Exception as X:
40 raise _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 else: # iterable?
193 _xiterable(other)
194 self.fadd(other)
195 except Exception as x:
196 t = _SPACE_(self, _iadd_op_, repr(other))
197 raise _xError(x, t)
199 @property_RO
200 def _M1(self):
201 '''(INTERNAL) get the 1st Moment accumulator.'''
202 return self._Ms[0]
204 @property_RO
205 def _M2(self):
206 '''(INTERNAL) get the 2nd Moment accumulator.'''
207 return self._Ms[1]
209 def _Mean(self, xs=None):
210 '''(INTERNAL) Return the current mean as L{Fsum}.
211 '''
212 if xs:
213 self.fadd(xs)
214 return self._M1 # .copy()
216 def _Stdev(self, xs=None, **sample):
217 '''(INTERNAL) Return the current (sample) standard deviation as L{Fsum}.
218 '''
219 V = self._Variance(xs, **sample)
220 return Fsqrt(V) if V > 0 else _0_0
222 def _Variance(self, xs=None, **sample):
223 '''(INTERNAL) Return the current (sample) variance as L{Fsum}.
224 '''
225 n = self.fadd(xs, **sample)
226 return (self._M2 / n) if n > 0 else _0_0
229class Fcook(_FstatsBase):
230 '''U{Cook<https://www.JohnDCook.com/blog/skewness_kurtosis>}'s
231 C{RunningStats} computing the running mean, median and
232 (sample) kurtosis, skewness, variance, standard deviation
233 and Jarque-Bera normality.
235 @see: L{Fwelford} and U{Higher-order statistics<https://
236 WikiPedia.org/wiki/Algorithms_for_calculating_variance>}.
237 '''
238 def __init__(self, xs=None, name=NN):
239 '''New L{Fcook} stats accumulator.
241 @arg xs: Iterable of additional values (each C{scalar} or
242 an L{Fsum} or L{Fsum2Tuple} instance).
243 @kwarg name: Optional name (C{str}).
245 @see: Method L{Fcook.fadd}.
246 '''
247 self._Ms = _Tuple(Fsum() for _ in range(4)) # 1st, 2nd ... Moment
248 if name:
249 self.name = name
250 if xs:
251 self.fadd(xs)
253 def __iadd__(self, other):
254 '''Add B{C{other}} to this L{Fcook} instance.
256 @arg other: An L{Fcook} instance or value or iterable
257 of values (each C{scalar} or an L{Fsum}
258 or L{Fsum2Tuple} instance).
260 @return: This instance, updated (L{Fcook}).
262 @raise TypeError: Invalid B{C{other}}.
264 @raise ValueError: Invalid or non-finite B{C{other}}.
266 @see: Method L{Fcook.fadd}.
267 '''
268 if _isAn(other, Fcook):
269 nb = len(other)
270 if nb > 0:
271 na = len(self)
272 if na > 0:
273 A1, A2, A3, A4 = self._Ms
274 B1, B2, B3, B4 = other._Ms
276 n = na + nb
277 _n = _1_0 / n
278 D = A1 - B1 # b1 - a1
279 Dn = D * _n
280 Dn2 = Dn**2 # d**2 / n**2
281 nab = na * nb
282 Dn3 = Dn2 * (D * nab)
284 na2 = na**2
285 nb2 = nb**2
286 A4 += B4
287 A4 += (B3 * na - (A3 * nb)) * (Dn * _4_0)
288 A4 += (B2 * na2 + (A2 * nb2)) * (Dn2 * _6_0)
289 A4 += (Dn * Dn3) * (na2 - nab + nb2) # d**4 / n**3
291 A3 += B3
292 A3 += (A2 * na - (B2 * nb)) * (Dn * _3_0)
293 A3 += Dn3 * (na - nb)
295 A2 += B2
296 A2 += Dn2 * (nab * _n)
298 B1n = B1 * nb # if other is self
299 A1 *= na
300 A1 += B1n
301 A1 *= _n
303# self._Ms = A1, A2, A3, A4
304 self._n = n
305 else:
306 self._copy(self, other)
307 else:
308 self._iadd_other(other)
309 return self
311 def fadd(self, xs, sample=False):
312 '''Accumulate and return the current count.
314 @arg xs: Iterable of additional values (each C{scalar} or an
315 L{Fsum} or L{Fsum2Tuple} instance).
316 @kwarg sample: Use C{B{sample}=True} for the I{sample} count
317 instead of the I{population} count (C{bool}).
319 @return: Current, running (sample) count (C{int}).
321 @raise OverflowError: Partial C{2sum} overflow.
323 @raise TypeError: Invalid B{C{xs}}.
325 @raise ValueError: Invalid or non-finite B{C{xs}}.
327 @see: U{online_kurtosis<https://WikiPedia.org/wiki/
328 Algorithms_for_calculating_variance>}.
329 '''
330 n = self._n
331 if xs:
332 M1, M2, M3, M4 = self._Ms
333 for x in _2Floats(xs=xs): # PYCHOK yield
334 n1 = n
335 n += 1
336 D = x - M1
337 Dn = D / n
338 if Dn:
339 Dn2 = Dn**2
340 if n1 > 1:
341 T1 = D * (Dn * n1)
342 T2 = T1 * (Dn * (n1 - 1))
343 T3 = T1 * (Dn2 * (n**2 - 3 * n1))
344 elif n1 > 0: # n1 == 1, n == 2
345 T1 = D * Dn
346 T2 = _0_0
347 T3 = T1 * Dn2
348 else:
349 T1 = T2 = T3 = _0_0
350 M4 += T3
351 M4 -= M3 * (Dn * _4_0)
352 M4 += M2 * (Dn2 * _6_0)
354 M3 += T2
355 M3 -= M2 * (Dn * _3_0)
357 M2 += T1
358 M1 += Dn
359# self._Ms = M1, M2, M3, M4
360 self._n = n
361 return _sampled(n, sample)
363 def fjb(self, xs=None, excess=True, sample=True):
364 '''Accumulate and compute the current U{Jarque-Bera
365 <https://WikiPedia.org/wiki/Jarque–Bera_test>} normality.
367 @kwarg xs: Iterable of additional values (each C{scalar} or an
368 L{Fsum} or L{Fsum2Tuple}).
369 @kwarg excess: Apply the I{excess} kurtosis (C{bool}), default.
370 @kwarg sample: Use C{B{sample}=False} for the I{population}
371 normality instead of the I{sample} one (C{bool}).
373 @return: Current, running (sample) Jarque-Bera normality (C{float}).
375 @see: Method L{Fcook.fadd}.
376 '''
377 return _Float(self._JarqueBera(xs, excess, sample=sample))
379 def fjb_(self, *xs, **sample_excess):
380 '''Accumulate and compute the current U{Jarque-Bera
381 <https://WikiPedia.org/wiki/Jarque–Bera_test>} normality.
383 @see: Method L{Fcook.fjb} for further details.
384 '''
385 return self.fjb(xs, **sample_excess)
387 def fkurtosis(self, xs=None, excess=True, **sample):
388 '''Accumulate and return the current kurtosis.
390 @arg xs: Iterable of additional values (each C{scalar} or an
391 L{Fsum} or L{Fsum2Tuple} instance).
392 @kwarg excess: Return the I{excess} kurtosis (C{bool}), default.
393 @kwarg sample: Use C{B{sample}=True} for the I{sample} kurtosis
394 instead of the I{population} kurtosis (C{bool}).
396 @return: Current, running (sample) kurtosis or I{excess} kurtosis (C{float}).
398 @see: U{Kurtosis Formula<https://www.Macroption.com/kurtosis-formula>}
399 and U{Mantalos<https://www.ResearchGate.net/publication/227440210>}.
401 @see: Method L{Fcook.fadd}.
402 '''
403 n = self.fadd(xs, **sample)
404 return _Float(self._Kurtosis(n, excess, **sample))
406 def fkurtosis_(self, *xs, **excess_sample):
407 '''Accumulate and return the current kurtosis.
409 @see: Method L{Fcook.fkurtosis} for further details.
410 '''
411 return self.fkurtosis(xs, **excess_sample)
413 def fmedian(self, xs=None):
414 '''Accumulate and return the current median.
416 @arg xs: Iterable of additional values (each C{scalar} or an
417 L{Fsum} or L{Fsum2Tuple} instance).
419 @return: Current, running median (C{float}).
421 @see: U{Pearson's Skewness Coefficients<https://MathWorld.Wolfram.com/
422 PearsonsSkewnessCoefficients.html>}, U{Skewness & Kurtosis Simplified
423 https://TowardsDataScience.com/skewness-kurtosis-simplified-1338e094fc85>}
424 and method L{Fcook.fadd}.
425 '''
426 return _Float(self._Median(xs))
428 def fmedian_(self, *xs):
429 '''Accumulate and return the current median.
431 @see: Method L{Fcook.fmedian} for further details.
432 '''
433 return self.fmedian(xs)
435 def fskewness(self, xs=None, **sample):
436 '''Accumulate and return the current skewness.
438 @arg xs: Iterable of additional values (each C{scalar} or an
439 L{Fsum} or L{Fsum2Tuple} instance).
440 @kwarg sample: Use C{B{sample}=True} for the I{sample} skewness
441 instead of the I{population} skewness (C{bool}).
443 @return: Current, running (sample) skewness (C{float}).
445 @see: U{Skewness Formula<https://www.Macroption.com/skewness-formula/>}
446 and U{Mantalos<https://www.ResearchGate.net/publication/227440210>}.
448 @see: Method L{Fcook.fadd}.
449 '''
450 n = self.fadd(xs, **sample)
451 return _Float(self._Skewness(n, **sample))
453 def fskewness_(self, *xs, **sample):
454 '''Accumulate and return the current skewness.
456 @see: Method L{Fcook.fskewness} for further details.
457 '''
458 return self.fskewness(xs, **sample)
460 def _JarqueBera(self, xs, excess, **sample):
461 '''(INTERNAL) Return the (sample) Jarque-Bera normality as L{Fsum}.
462 '''
463 N, n = _0_0, self.fadd(xs, **sample)
464 if n > 0:
465 K = self._Kurtosis(n, excess, **sample) / _2_0
466 S = self._Skewness(n, **sample)
467 N = (K**2 + S**2) * (n / _6_0) # Fpowers(2, K, S) * ...
468 return N
470 def _Kurtosis(self, n, excess, sample=False):
471 '''(INTERNAL) Return the (sample) kurtosis as L{Fsum} or C{0.0}.
472 '''
473 K = _0_0
474 if n > 0:
475 _, M2, _, M4 = self._Ms
476 M = M2**2
477 if M > 0:
478 K, x = M.rdiv(M4 * n, raiser=False), _3_0
479 if sample and 2 < n < len(self):
480 d = (n - 1) * (n - 2)
481 K *= (n + 1) * (n + 2) / d
482 x *= n**2 / d
483 if excess:
484 K -= x
485 return K
487 def _Median(self, xs=None):
488 '''(INTERNAL) Return the median as L{Fsum}.
489 '''
490 # skewness = 3 * (mean - median) / stdev, i.e.
491 # median = mean - (skewness * stdef) / 3
492 return self._Mean(xs) - (self._Skewness(self._n) *
493 self._Stdev()) / _3_0
495 def _Skewness(self, n, sample=False):
496 '''(INTERNAL) Return the (sample) skewness as L{Fsum} or C{0.0}.
497 '''
498 S = _0_0
499 if n > 0:
500 _, M2, M3, _ = self._Ms
501 M = M2**3
502 if M > 0:
503 M = M.rdiv(n, raiser=False)
504 S = M3 * Fsqrt(M, raiser=False)
505 if sample and 1 < n < len(self):
506 S *= (n + 1) / (n - 1)
507 return S
509 def toFwelford(self, name=NN):
510 '''Return an L{Fwelford} equivalent.
511 '''
512 f = Fwelford(name=name or self.name)
513 f._Ms = self._M1.copy(), self._M2.copy() # deep=False
514 f._n = self._n
515 return f
518class Fwelford(_FstatsBase):
519 '''U{Welford<https://WikiPedia.org/wiki/Algorithms_for_calculating_variance>}'s
520 accumulator computing the running mean, (sample) variance and standard deviation.
522 @see: U{Cook<https://www.JohnDCook.com/blog/standard_deviation/>} and L{Fcook}.
523 '''
524 def __init__(self, xs=None, name=NN):
525 '''New L{Fwelford} stats accumulator.
527 @arg xs: Iterable of initial values (each C{scalar} or an
528 L{Fsum} or L{Fsum2Tuple} instance).
529 @kwarg name: Optional name (C{str}).
531 @see: Method L{Fwelford.fadd}.
532 '''
533 self._Ms = Fsum(), Fsum() # 1st and 2nd Moment
534 if name:
535 self.name = name
536 if xs:
537 self.fadd(xs)
539 def __iadd__(self, other):
540 '''Add B{C{other}} to this L{Fwelford} instance.
542 @arg other: An L{Fwelford} or L{Fcook} instance or value
543 or an iterable of values (each C{scalar} or
544 an L{Fsum} or L{Fsum2Tuple} instance).
546 @return: This instance, updated (L{Fwelford}).
548 @raise TypeError: Invalid B{C{other}}.
550 @raise ValueError: Invalid B{C{other}}.
552 @see: Method L{Fwelford.fadd} and U{Parallel algorithm<https//
553 WikiPedia.org/wiki/Algorithms_for_calculating_variance>}.
554 '''
555 if _isAn(other, Fwelford):
556 nb = len(other)
557 if nb > 0:
558 na = len(self)
559 if na > 0:
560 M, S = self._Ms
561 M_, S_ = other._Ms
563 n = na + nb
564 _n = _1_0 / n
566 D = M_ - M
567 D *= D # D**2
568 D *= na * nb * _n
569 S += D
570 S += S_
572 Mn = M_ * nb # if other is self
573 M *= na
574 M += Mn
575 M *= _n
577# self._Ms = M, S
578 self._n = n
579 else:
580 self._copy(self, other)
582 elif _isAn(other, Fcook):
583 self += other.toFwelford()
584 else:
585 self._iadd_other(other)
586 return self
588 def fadd(self, xs, sample=False):
589 '''Accumulate and return the current count.
591 @arg xs: Iterable of additional values (each C{scalar} or an
592 L{Fsum} or L{Fsum2Tuple} instance).
593 @kwarg sample: Use C{B{sample}=True} for the I{sample} count
594 instead of the I{population} count (C{bool}).
596 @return: Current, running (sample) count (C{int}).
598 @raise OverflowError: Partial C{2sum} overflow.
600 @raise TypeError: Invalid B{C{xs}}.
602 @raise ValueError: Invalid or non-finite B{C{xs}}.
603 '''
604 n = self._n
605 if xs:
606 M, S = self._Ms
607 for x in _2Floats(xs=xs): # PYCHOK yield
608 n += 1
609 D = x - M
610 M += D / n
611 D *= x - M
612 S += D
613# self._Ms = M, S
614 self._n = n
615 return _sampled(n, sample)
618class Flinear(_FstatsNamed):
619 '''U{Cook<https://www.JohnDCook.com/blog/running_regression>}'s
620 C{RunningRegression} computing the running slope, intercept
621 and correlation of a linear regression.
622 '''
623 def __init__(self, xs=None, ys=None, Fstats=Fwelford, name=NN):
624 '''New L{Flinear} regression accumulator.
626 @kwarg xs: Iterable of initial C{x} values (each C{scalar} or
627 an L{Fsum} or L{Fsum2Tuple} instance).
628 @kwarg ys: Iterable of initial C{y} values (each C{scalar} or
629 an L{Fsum} or L{Fsum2Tuple} instance).
630 @kwarg Fstats: Class for C{xs} and C{ys} values (L{Fcook} or
631 L{Fwelford}).
632 @kwarg name: Optional name (C{str}).
634 @raise TypeError: B{C{Fstats}} not L{Fcook} or L{Fwelford}.
636 @see: Method L{Flinear.fadd}.
637 '''
638 _xsubclassof(Fcook, Fwelford, Fstats=Fstats)
639 if name:
640 self.name = name
642 self._S = Fsum(name=name)
643 self._X = Fstats(name=name)
644 self._Y = Fstats(name=name)
645 if xs and ys:
646 self.fadd(xs, ys)
648 def __iadd__(self, other):
649 '''Add B{C{other}} to this instance.
651 @arg other: An L{Flinear} instance or an iterable of
652 C{x_ys} values, see method C{fadd_}.
654 @return: This instance, updated (L{Flinear}).
656 @raise TypeError: Invalid B{C{other}} or the B{C{other}}
657 and these C{x} and C{y} accumulators
658 are not compatible.
660 @raise ValueError: Invalid or odd-length B{C{other}}.
662 @see: Method L{Flinear.fadd_}.
663 '''
664 if _isAn(other, Flinear):
665 if len(other) > 0:
666 if len(self) > 0:
667 n = other._n
668 D = (other._X._M1 - self._X._M1) * \
669 (other._Y._M1 - self._Y._M1) * \
670 (n * self._n / (self._n + n))
671 self._S += other._S + D
672 self._X += other._X
673 self._Y += other._Y
674 self._n += n
675 else:
676 self._copy(self, other)
677 else:
678 try:
679 _xiterable(other)
680 self.fadd_(*other)
681 except Exception as x:
682 op = _SPACE_(self, _iadd_op_, repr(other))
683 raise _xError(x, op)
684 return self
686 def _copy(self, d, s):
687 '''(INTERNAL) Copy C{B{d} = B{s}}.
688 '''
689 _xinstanceof(Flinear, d=d, s=s)
690 d._S = s._S.copy(deep=False)
691 d._X = s._X.copy(deep=False)
692 d._Y = s._Y.copy(deep=False)
693 d._n = s._n
694 return d
696 def _Correlation(self, **sample):
697 '''(INTERNAL) Return the current (sample) correlation as L{Fsum}.
698 '''
699 return self._Sampled(self._X._Stdev(**sample) *
700 self._Y._Stdev(**sample), **sample)
702 def fadd(self, xs, ys, sample=False):
703 '''Accumulate and return the current count.
705 @arg xs: Iterable of additional C{x} values (each C{scalar}
706 or an L{Fsum} or L{Fsum2Tuple} instance).
707 @arg ys: Iterable of additional C{y} values (each C{scalar}
708 or an L{Fsum} or L{Fsum2Tuple} instance).
709 @kwarg sample: Use C{B{sample}=True} for the I{sample} count
710 instead of the I{population} count (C{bool}).
712 @return: Current, running (sample) count (C{int}).
714 @raise OverflowError: Partial C{2sum} overflow.
716 @raise TypeError: Invalid B{C{xs}} or B{C{ys}}.
718 @raise ValueError: Invalid or non-finite B{C{xs}} or B{C{ys}}.
719 '''
720 n = self._n
721 if xs and ys:
722 S = self._S
723 X = self._X
724 Y = self._Y
725 for x, y in _zip(_2Floats(xs=xs), _2Floats(ys=ys)): # PYCHOK strict=True
726 n1 = n
727 n += 1
728 if n1 > 0:
729 S += (X._M1 - x) * (Y._M1 - y) * (n1 / n)
730 X += x
731 Y += y
732 self._n = n
733 return _sampled(n, sample)
735 def fadd_(self, *x_ys, **sample):
736 '''Accumulate and return the current count.
738 @arg x_ys: Individual, alternating C{x, y, x, y, ...} values
739 (each C{scalar} or an L{Fsum} or L{Fsum2Tuple}
740 instance).
742 @see: Method C{Flinear.fadd} for further details.
743 '''
744 if isodd(len(x_ys)):
745 t = _SPACE_(_odd_, len.__name__)
746 raise _ValueError(t, len(x_ys))
747 return self.fadd(x_ys[0::2], x_ys[1::2], **sample)
749 def fcorrelation(self, **sample):
750 '''Return the current, running (sample) correlation (C{float}).
752 @kwarg sample: Use C{B{sample}=True} for the I{sample} correlation
753 instead of the I{population} correlation (C{bool}).
754 '''
755 return _Float(self._Correlation(**sample))
757 def fintercept(self, **sample):
758 '''Return the current, running (sample) intercept (C{float}).
760 @kwarg sample: Use C{B{sample}=True} for the I{sample} intercept
761 instead of the I{population} intercept (C{bool}).
762 '''
763 return _Float(self._Intercept(**sample))
765 def fslope(self, **sample):
766 '''Return the current, running (sample) slope (C{float}).
768 @kwarg sample: Use C{B{sample}=True} for the I{sample} slope
769 instead of the I{population} slope (C{bool}).
770 '''
771 return _Float(self._Slope(**sample))
773 def _Intercept(self, **sample):
774 '''(INTERNAL) Return the current (sample) intercept as L{Fsum}.
775 '''
776 return self._Y._M1 - self._X._M1 * self._Slope(**sample)
778 def _Sampled(self, T, sample=False):
779 '''(INTERNAL) Compute the sampled or entire population result.
780 '''
781 T *= _sampled(self._n, sample)
782 return self._S.copy().fdiv(T, raiser=False) if T else T
784 def _Slope(self, **sample):
785 '''(INTERNAL) Return the current (sample) slope as L{Fsum}.
786 '''
787 return self._Sampled(self._X._Variance(**sample), **sample)
789 @property_RO
790 def x(self):
791 '''Get the C{x} accumulator (L{Fcook} or L{Fwelford}).
792 '''
793 return self._X # .copy()
795 @property_RO
796 def y(self):
797 '''Get the C{y} accumulator (L{Fcook} or L{Fwelford}).
798 '''
799 return self._Y # .copy()
802__all__ += _ALL_DOCS(_FstatsBase, _FstatsNamed)
804# **) MIT License
805#
806# Copyright (C) 2021-2024 -- mrJean1 at Gmail -- All Rights Reserved.
807#
808# Permission is hereby granted, free of charge, to any person obtaining a
809# copy of this software and associated documentation files (the "Software"),
810# to deal in the Software without restriction, including without limitation
811# the rights to use, copy, modify, merge, publish, distribute, sublicense,
812# and/or sell copies of the Software, and to permit persons to whom the
813# Software is furnished to do so, subject to the following conditions:
814#
815# The above copyright notice and this permission notice shall be included
816# in all copies or substantial portions of the Software.
817#
818# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
819# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
820# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
821# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
822# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
823# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
824# OTHER DEALINGS IN THE SOFTWARE.