Coverage for pygeodesy/fstats.py: 98%
325 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-04-25 13:15 -0400
« prev ^ index » next coverage.py v7.6.1, created at 2025-04-25 13:15 -0400
2# -*- coding: utf-8 -*-
4u'''Classes for I{running} statistics and regressions based on
5L{pygeodesy.Fsum}, precision floating point summation and accurate
6multiplication.
7'''
8# make sure int/int division yields float quotient, see .basics
9from __future__ import division as _; del _ # PYCHOK semicolon
11from pygeodesy.basics import isscalar, isodd, _xinstanceof, \
12 _xiterable, _xsubclassof, _zip, typename
13from pygeodesy.constants import _0_0, _1_0, _2_0, _3_0, _4_0, _6_0
14from pygeodesy.errors import _ValueError, _xError, _xkwds_item2, \
15 _xsError
16from pygeodesy.fmath import Fsqrt, Fmt
17from pygeodesy.fsums import _2finite, Fsum, _iadd_op_, _isFsum_2Tuple
18# from pygeodesy.internals import typename # from .basics
19from pygeodesy.interns import _odd_, _SPACE_
20from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY
21from pygeodesy.named import _name__, _Named, _NotImplemented, \
22 property_RO
23# from pygeodesy.props import property_RO # from .named
24# from pygeodesy.streprs import Fmt # from .fmath
26__all__ = _ALL_LAZY.fstats
27__version__ = '25.04.14'
30def _sampled(n, sample):
31 '''(INTERNAL) Return the sample or the entire count.
32 '''
33 return (n - 1) if sample and n > 0 else n
36def _Xs(**name_xs):
37 '''(INTERNAL) Yield all C{xs} as C{float} or L{Fsum}.
38 '''
39 name, xs = _xkwds_item2(name_xs)
40 try:
41 i, x = 0, xs
42 for i, x in enumerate(xs): # don't unravel Fsums
43 yield x._Fsum if _isFsum_2Tuple(x) else x
44 except Exception as X:
45 raise _xsError(X, xs, i, x, name)
48class _FstatsNamed(_Named):
49 '''(INTERNAL) Base class.
50 '''
51 _n = 0
53 def __add__(self, other):
54 '''Sum of this and an other instance, a C{scalar}, an L{Fsum}
55 or L{Fsum2Tuple}.
56 '''
57 f = self.copy(name__=self.__add__) # PYCHOK expected
58 f += other
59 return f
61 def __float__(self): # PYCHOK no cover
62 '''Not implemented.'''
63 return _NotImplemented(self)
65 def __int__(self): # PYCHOK no cover
66 '''Not implemented.'''
67 return _NotImplemented(self)
69 def __len__(self):
70 '''Return the I{total} number of accumulated C{Scalars} (C{int}).
71 '''
72 return self._n
74 def __neg__(self): # PYCHOK no cover
75 '''Not implemented.'''
76 return _NotImplemented(self)
78 def __radd__(self, other): # PYCHOK no cover
79 '''Not implemented.'''
80 return _NotImplemented(self, other)
82 def __str__(self):
83 n = self.name
84 n = _SPACE_(self.classname, n) if n else self.classname
85 return Fmt.SQUARE(n, len(self))
87 def copy(self, deep=False, **name):
88 '''Copy this instance, C{shallow} or B{C{deep}}.
90 @kwarg name: Optional, overriding C{B{name}="copy"} (C{str}).
92 @return: The copy instance.
93 '''
94 n = _name__(name, name__=self.copy)
95 f = _Named.copy(self, deep=deep, name=n)
96 return self._copy(f, self) # PYCHOK expected
98 fcopy = copy # for backward compatibility
101class _FstatsBase(_FstatsNamed):
102 '''(INTERNAL) Base running stats class.
103 '''
104 _Ms = ()
106 def _copy(self, d, s):
107 '''(INTERNAL) Copy C{B{d} = B{s}}.
108 '''
109 _xinstanceof(self.__class__, d=d, s=s)
110 d._Ms = tuple(M.copy() for M in s._Ms) # deep=False
111 d._n = s._n
112 return d
114 def fadd(self, xs, sample=False): # PYCHOK no cover
115 '''I{Must be overloaded}.'''
116 self._notOverloaded(xs, sample=sample)
118 def fadd_(self, *xs, **sample):
119 '''Accumulate and return the current count.
121 @see: Method C{fadd} for further details.
122 '''
123 return self.fadd(xs, **sample)
125 def fmean(self, xs=None):
126 '''Accumulate and return the current mean.
128 @kwarg xs: Iterable of additional values (each C{scalar},
129 an L{Fsum} or L{Fsum2Tuple}).
131 @return: Current, running mean (C{float}).
133 @see: Method C{fadd}.
134 '''
135 return float(self._Mean(xs))
137 def fmean_(self, *xs):
138 '''Accumulate and return the current mean.
140 @see: Method C{fmean} for further details.
141 '''
142 return self.fmean(xs)
144 def fstdev(self, xs=None, **sample):
145 '''Accumulate and return the current standard deviation.
147 @arg xs: Iterable of additional values (each C{scalar}, an L{Fsum}
148 or L{Fsum2Tuple}).
149 @kwarg sample: Use C{B{sample}=True} for the I{sample} deviation
150 instead of the I{population} deviation (C{bool}).
152 @return: Current, running (sample) standard deviation (C{float}).
154 @see: Method C{fadd}.
155 '''
156 return float(self._Stdev(xs, **sample))
158 def fstdev_(self, *xs, **sample):
159 '''Accumulate and return the current standard deviation.
161 @see: Method C{fstdev} for further details.
162 '''
163 return self.fstdev(xs, **sample)
165 def fvariance(self, xs=None, **sample):
166 '''Accumulate and return the current variance.
168 @arg xs: Iterable of additional values (each C{scalar}, an L{Fsum}
169 or L{Fsum2Tuple}).
170 @kwarg sample: Use C{B{sample}=True} for the I{sample} variance
171 instead of the I{population} variance (C{bool}).
173 @return: Current, running (sample) variance (C{float}).
175 @see: Method C{fadd}.
176 '''
177 return float(self._Variance(xs, **sample))
179 def fvariance_(self, *xs, **sample):
180 '''Accumulate and return the current variance.
182 @see: Method C{fvariance} for further details.
183 '''
184 return self.fvariance(xs, **sample)
186 def _iadd_other(self, other):
187 '''(INTERNAL) Add one or several values.
188 '''
189 try:
190 if _isFsum_2Tuple(other):
191 self.fadd_(other._Fsum)
192 elif isscalar(other):
193 self.fadd_(_2finite(other))
194 elif _xiterable(other):
195 self.fadd(other)
196 except Exception as X:
197 t = _SPACE_(self, _iadd_op_, repr(other))
198 raise _xError(X, t)
200 @property_RO
201 def _M1(self):
202 '''(INTERNAL) get the 1st Moment accumulator.'''
203 return self._Ms[0]
205 @property_RO
206 def _M2(self):
207 '''(INTERNAL) get the 2nd Moment accumulator.'''
208 return self._Ms[1]
210 def _Mean(self, xs=None):
211 '''(INTERNAL) Return the current mean as L{Fsum}.
212 '''
213 if xs:
214 self.fadd(xs)
215 return self._M1 # .copy()
217 def _Stdev(self, xs=None, **sample):
218 '''(INTERNAL) Return the current (sample) standard deviation as L{Fsum}.
219 '''
220 V = self._Variance(xs, **sample)
221 return Fsqrt(V) if V > 0 else _0_0
223 def _Variance(self, xs=None, **sample):
224 '''(INTERNAL) Return the current (sample) variance as L{Fsum}.
225 '''
226 n = self.fadd(xs, **sample)
227 return (self._M2 / n) if n > 0 else _0_0
230class Fcook(_FstatsBase):
231 '''U{Cook<https://www.JohnDCook.com/blog/skewness_kurtosis>}'s
232 C{RunningStats} computing the running mean, median and
233 (sample) kurtosis, skewness, variance, standard deviation
234 and Jarque-Bera normality.
236 @see: L{Fwelford} and U{Higher-order statistics<https://
237 WikiPedia.org/wiki/Algorithms_for_calculating_variance>}.
238 '''
239 def __init__(self, xs=None, **name):
240 '''New L{Fcook} stats accumulator.
242 @arg xs: Iterable of additional values (each C{scalar}, an
243 L{Fsum} or L{Fsum2Tuple}).
244 @kwarg name: Optional C{B{name}=NN} (C{str}).
246 @see: Method L{Fcook.fadd}.
247 '''
248 self._Ms = tuple(Fsum() for _ in range(4)) # 1st, 2nd ... Moment
249 if name:
250 self.name = name
251 if xs:
252 self.fadd(xs)
254 def __iadd__(self, other):
255 '''Add B{C{other}} to this L{Fcook} instance.
257 @arg other: An L{Fcook} instance or value or iterable
258 of values (each C{scalar}, an L{Fsum} or
259 L{Fsum2Tuple}).
261 @return: This instance, updated (L{Fcook}).
263 @raise TypeError: Invalid B{C{other}}.
265 @raise ValueError: Invalid or non-finite B{C{other}}.
267 @see: Method L{Fcook.fadd}.
268 '''
269 if isinstance(other, Fcook):
270 nb = len(other)
271 if nb > 0:
272 na = len(self)
273 if na > 0:
274 A1, A2, A3, A4 = self._Ms
275 B1, B2, B3, B4 = other._Ms
277 n = na + nb
278 _n = _1_0 / n
279 D = A1 - B1 # b1 - a1
280 Dn = D * _n
281 Dn2 = Dn**2 # d**2 / n**2
282 nab = na * nb
283 Dn3 = Dn2 * (D * nab)
285 na2 = na**2
286 nb2 = nb**2
287 A4 += B4
288 A4 += (B3 * na - (A3 * nb)) * (Dn * _4_0)
289 A4 += (B2 * na2 + (A2 * nb2)) * (Dn2 * _6_0)
290 A4 += (Dn * Dn3) * (na2 - nab + nb2) # d**4 / n**3
292 A3 += B3
293 A3 += (A2 * na - (B2 * nb)) * (Dn * _3_0)
294 A3 += Dn3 * (na - nb)
296 A2 += B2
297 A2 += Dn2 * (nab * _n)
299 B1n = B1 * nb # if other is self
300 A1 *= na
301 A1 += B1n
302 A1 *= _n
304# self._Ms = A1, A2, A3, A4
305 self._n = n
306 else:
307 self._copy(self, other)
308 else:
309 self._iadd_other(other)
310 return self
312 def fadd(self, xs, sample=False):
313 '''Accumulate and return the current count.
315 @arg xs: Iterable of additional values (each C{scalar}, an
316 L{Fsum} or L{Fsum2Tuple}).
317 @kwarg sample: Use C{B{sample}=True} for the I{sample} count
318 instead of the I{population} count (C{bool}).
320 @return: Current, running (sample) count (C{int}).
322 @raise OverflowError: Partial C{2sum} overflow.
324 @raise TypeError: Invalid B{C{xs}}.
326 @raise ValueError: Invalid or non-finite B{C{xs}}.
328 @see: U{online_kurtosis<https://WikiPedia.org/wiki/
329 Algorithms_for_calculating_variance>}.
330 '''
331 n = self._n
332 if xs:
333 M1, M2, M3, M4 = self._Ms
334 for x in _Xs(xs=xs): # PYCHOK yield
335 n1 = n
336 n += 1
337 D = x - M1
338 Dn = D / n
339 if Dn:
340 Dn2 = Dn**2
341 if n1 > 1:
342 T1 = D * (Dn * n1)
343 T2 = T1 * (Dn * (n1 - 1))
344 T3 = T1 * (Dn2 * (n**2 - 3 * n1))
345 elif n1 > 0: # n1 == 1, n == 2
346 T1 = D * Dn
347 T2 = _0_0
348 T3 = T1 * Dn2
349 else:
350 T1 = T2 = T3 = _0_0
351 M4 += T3
352 M4 -= M3 * (Dn * _4_0)
353 M4 += M2 * (Dn2 * _6_0)
355 M3 += T2
356 M3 -= M2 * (Dn * _3_0)
358 M2 += T1
359 M1 += Dn
360# self._Ms = M1, M2, M3, M4
361 self._n = n
362 return _sampled(n, sample)
364 def fjb(self, xs=None, excess=True, sample=True):
365 '''Accumulate and compute the current U{Jarque-Bera
366 <https://WikiPedia.org/wiki/Jarque–Bera_test>} normality.
368 @kwarg xs: Iterable of additional values (each C{scalar}, an
369 L{Fsum} or L{Fsum2Tuple}).
370 @kwarg excess: Apply the I{excess} kurtosis (C{bool}), default.
371 @kwarg sample: Use C{B{sample}=False} for the I{population}
372 normality instead of the I{sample} one (C{bool}).
374 @return: Current, running (sample) Jarque-Bera normality (C{float}).
376 @see: Method L{Fcook.fadd}.
377 '''
378 return float(self._JarqueBera(xs, excess, sample=sample))
380 def fjb_(self, *xs, **sample_excess):
381 '''Accumulate and compute the current U{Jarque-Bera
382 <https://WikiPedia.org/wiki/Jarque–Bera_test>} normality.
384 @see: Method L{Fcook.fjb} for further details.
385 '''
386 return self.fjb(xs, **sample_excess)
388 def fkurtosis(self, xs=None, excess=True, **sample):
389 '''Accumulate and return the current kurtosis.
391 @arg xs: Iterable of additional values (each C{scalar}, an L{Fsum}
392 or L{Fsum2Tuple}).
393 @kwarg excess: Return the I{excess} kurtosis (C{bool}), default.
394 @kwarg sample: Use C{B{sample}=True} for the I{sample} kurtosis
395 instead of the I{population} kurtosis (C{bool}).
397 @return: Current, running (sample) kurtosis or I{excess} kurtosis (C{float}).
399 @see: U{Kurtosis Formula<https://www.Macroption.com/kurtosis-formula>}
400 and U{Mantalos<https://www.ResearchGate.net/publication/227440210>}.
402 @see: Method L{Fcook.fadd}.
403 '''
404 n = self.fadd(xs, **sample)
405 return float(self._Kurtosis(n, excess, **sample))
407 def fkurtosis_(self, *xs, **excess_sample):
408 '''Accumulate and return the current kurtosis.
410 @see: Method L{Fcook.fkurtosis} for further details.
411 '''
412 return self.fkurtosis(xs, **excess_sample)
414 def fmedian(self, xs=None):
415 '''Accumulate and return the current median.
417 @arg xs: Iterable of additional values (each C{scalar}, an L{Fsum} or
418 L{Fsum2Tuple}).
420 @return: Current, running median (C{float}).
422 @see: U{Pearson's Skewness Coefficients<https://MathWorld.Wolfram.com/
423 PearsonsSkewnessCoefficients.html>}, U{Skewness & Kurtosis Simplified
424 https://TowardsDataScience.com/skewness-kurtosis-simplified-1338e094fc85>}
425 and method L{Fcook.fadd}.
426 '''
427 return float(self._Median(xs))
429 def fmedian_(self, *xs):
430 '''Accumulate and return the current median.
432 @see: Method L{Fcook.fmedian} for further details.
433 '''
434 return self.fmedian(xs)
436 def fskewness(self, xs=None, **sample):
437 '''Accumulate and return the current skewness.
439 @arg xs: Iterable of additional values (each C{scalar}, an L{Fsum}
440 or L{Fsum2Tuple}).
441 @kwarg sample: Use C{B{sample}=True} for the I{sample} skewness
442 instead of the I{population} skewness (C{bool}).
444 @return: Current, running (sample) skewness (C{float}).
446 @see: U{Skewness Formula<https://www.Macroption.com/skewness-formula/>}
447 and U{Mantalos<https://www.ResearchGate.net/publication/227440210>}.
449 @see: Method L{Fcook.fadd}.
450 '''
451 n = self.fadd(xs, **sample)
452 return float(self._Skewness(n, **sample))
454 def fskewness_(self, *xs, **sample):
455 '''Accumulate and return the current skewness.
457 @see: Method L{Fcook.fskewness} for further details.
458 '''
459 return self.fskewness(xs, **sample)
461 def _JarqueBera(self, xs, excess, **sample):
462 '''(INTERNAL) Return the (sample) Jarque-Bera normality as L{Fsum}.
463 '''
464 N, n = _0_0, self.fadd(xs, **sample)
465 if n > 0:
466 K = self._Kurtosis(n, excess, **sample) / _2_0
467 S = self._Skewness(n, **sample)
468 N = (K**2 + S**2) * (n / _6_0) # Fpowers(2, K, S) * ...
469 return N
471 def _Kurtosis(self, n, excess, sample=False):
472 '''(INTERNAL) Return the (sample) kurtosis as L{Fsum} or C{0.0}.
473 '''
474 K = _0_0
475 if n > 0:
476 _, M2, _, M4 = self._Ms
477 M = M2**2
478 if M > 0:
479 K, x = M.rdiv(M4 * n, raiser=False), _3_0
480 if sample and 2 < n < len(self):
481 d = (n - 1) * (n - 2)
482 K *= (n + 1) * (n + 2) / d
483 x *= n**2 / d
484 if excess:
485 K -= x
486 return K
488 def _Median(self, xs=None):
489 '''(INTERNAL) Return the median as L{Fsum}.
490 '''
491 # skewness = 3 * (mean - median) / stdev, i.e.
492 # median = mean - (skewness * stdef) / 3
493 return self._Mean(xs) - (self._Skewness(self._n) *
494 self._Stdev()) / _3_0
496 def _Skewness(self, n, sample=False):
497 '''(INTERNAL) Return the (sample) skewness as L{Fsum} or C{0.0}.
498 '''
499 S = _0_0
500 if n > 0:
501 _, M2, M3, _ = self._Ms
502 M = M2**3
503 if M > 0:
504 M = M.rdiv(n, raiser=False)
505 S = M3 * Fsqrt(M, raiser=False)
506 if sample and 1 < n < len(self):
507 S *= (n + 1) / (n - 1)
508 return S
510 def toFwelford(self, **name):
511 '''Return a L{Fwelford} equivalent.
513 @kwarg name: Optional C{B{name}=NN} (C{str}).
514 '''
515 f = Fwelford(name=self._name__(name))
516 f._Ms = self._M1.copy(), self._M2.copy() # deep=False
517 f._n = self._n
518 return f
521class Fwelford(_FstatsBase):
522 '''U{Welford<https://WikiPedia.org/wiki/Algorithms_for_calculating_variance>}'s
523 accumulator computing the running mean, (sample) variance and standard deviation.
525 @see: U{Cook<https://www.JohnDCook.com/blog/standard_deviation/>} and L{Fcook}.
526 '''
527 def __init__(self, xs=None, **name):
528 '''New L{Fwelford} stats accumulator.
530 @arg xs: Iterable of initial values (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
531 @kwarg name: Optional C{B{name}=NN} (C{str}).
533 @see: Method L{Fwelford.fadd}.
534 '''
535 self._Ms = Fsum(), Fsum() # 1st and 2nd Moment
536 if name:
537 self.name = name
538 if xs:
539 self.fadd(xs)
541 def __iadd__(self, other):
542 '''Add B{C{other}} to this L{Fwelford} instance.
544 @arg other: An L{Fwelford} or L{Fcook} instance or value or an
545 iterable of values (each C{scalar}, an L{Fsum} or
546 L{Fsum2Tuple}).
548 @return: This instance, updated (L{Fwelford}).
550 @raise TypeError: Invalid B{C{other}}.
552 @raise ValueError: Invalid B{C{other}}.
554 @see: Method L{Fwelford.fadd} and U{Parallel algorithm<https//
555 WikiPedia.org/wiki/Algorithms_for_calculating_variance>}.
556 '''
557 if isinstance(other, Fwelford):
558 nb = len(other)
559 if nb > 0:
560 na = len(self)
561 if na > 0:
562 M, S = self._Ms
563 M_, S_ = other._Ms
565 n = na + nb
566 _n = _1_0 / n
568 D = M_ - M
569 D *= D # D**2
570 D *= na * nb * _n
571 S += D
572 S += S_
574 Mn = M_ * nb # if other is self
575 M *= na
576 M += Mn
577 M *= _n
579# self._Ms = M, S
580 self._n = n
581 else:
582 self._copy(self, other)
584 elif isinstance(other, Fcook):
585 self += other.toFwelford()
586 else:
587 self._iadd_other(other)
588 return self
590 def fadd(self, xs, sample=False):
591 '''Accumulate and return the current count.
593 @arg xs: Iterable of additional values (each C{scalar}, an
594 L{Fsum} or L{Fsum2Tuple}).
595 @kwarg sample: Use C{B{sample}=True} for the I{sample} count
596 instead of the I{population} count (C{bool}).
598 @return: Current, running (sample) count (C{int}).
600 @raise OverflowError: Partial C{2sum} overflow.
602 @raise TypeError: Invalid B{C{xs}}.
604 @raise ValueError: Invalid or non-finite B{C{xs}}.
605 '''
606 n = self._n
607 if xs:
608 M, S = self._Ms
609 for x in _Xs(xs=xs): # PYCHOK yield
610 n += 1
611 D = x - M
612 M += D / n
613 D *= x - M
614 S += D
615# self._Ms = M, S
616 self._n = n
617 return _sampled(n, sample)
620class Flinear(_FstatsNamed):
621 '''U{Cook<https://www.JohnDCook.com/blog/running_regression>}'s
622 C{RunningRegression} computing the running slope, intercept
623 and correlation of a linear regression.
624 '''
625 def __init__(self, xs=None, ys=None, Fstats=Fwelford, **name):
626 '''New L{Flinear} regression accumulator.
628 @kwarg xs: Iterable of initial C{x} values (each C{scalar},
629 an L{Fsum} or L{Fsum2Tuple}).
630 @kwarg ys: Iterable of initial C{y} values (each C{scalar},
631 an L{Fsum} or L{Fsum2Tuple}).
632 @kwarg Fstats: Class for C{xs} and C{ys} values (L{Fcook} or
633 L{Fwelford}).
634 @kwarg name: Optional C{B{name}=NN} (C{str}).
636 @raise TypeError: B{C{Fstats}} not L{Fcook} or L{Fwelford}.
638 @see: Method L{Flinear.fadd}.
639 '''
640 _xsubclassof(Fcook, Fwelford, Fstats=Fstats)
641 if name:
642 self.name = name
644 self._S = Fsum(name=name)
645 self._X = Fstats(name=name)
646 self._Y = Fstats(name=name)
647 if xs and ys:
648 self.fadd(xs, ys)
650 def __iadd__(self, other):
651 '''Add B{C{other}} to this instance.
653 @arg other: An L{Flinear} instance or an iterable of
654 C{x_ys} values, see method C{fadd_}.
656 @return: This instance, updated (L{Flinear}).
658 @raise TypeError: Invalid B{C{other}} or the B{C{other}}
659 and these C{x} and C{y} accumulators
660 are not compatible.
662 @raise ValueError: Invalid or odd-length B{C{other}}.
664 @see: Method L{Flinear.fadd_}.
665 '''
666 if isinstance(other, Flinear):
667 if len(other) > 0:
668 if len(self) > 0:
669 n = other._n
670 D = (other._X._M1 - self._X._M1) * \
671 (other._Y._M1 - self._Y._M1) * \
672 (n * self._n / (self._n + n))
673 self._S += other._S + D
674 self._X += other._X
675 self._Y += other._Y
676 self._n += n
677 else:
678 self._copy(self, other)
679 else:
680 try:
681 if _xiterable(other):
682 self.fadd_(*other)
683 except Exception as X:
684 op = _SPACE_(self, _iadd_op_, repr(other))
685 raise _xError(X, op)
686 return self
688 def _copy(self, d, s):
689 '''(INTERNAL) Copy C{B{d} = B{s}}.
690 '''
691 _xinstanceof(Flinear, d=d, s=s)
692 d._S = s._S.copy(deep=False)
693 d._X = s._X.copy(deep=False)
694 d._Y = s._Y.copy(deep=False)
695 d._n = s._n
696 return d
698 def _Correlation(self, **sample):
699 '''(INTERNAL) Return the current (sample) correlation as L{Fsum}.
700 '''
701 return self._Sampled(self._X._Stdev(**sample) *
702 self._Y._Stdev(**sample), **sample)
704 def fadd(self, xs, ys, sample=False):
705 '''Accumulate and return the current count.
707 @arg xs: Iterable of additional C{x} values (each C{scalar},
708 an L{Fsum} or L{Fsum2Tuple}).
709 @arg ys: Iterable of additional C{y} values (each C{scalar},
710 an L{Fsum} or L{Fsum2Tuple}).
711 @kwarg sample: Use C{B{sample}=True} for the I{sample} count
712 instead of the I{population} count (C{bool}).
714 @return: Current, running (sample) count (C{int}).
716 @raise OverflowError: Partial C{2sum} overflow.
718 @raise TypeError: Invalid B{C{xs}} or B{C{ys}}.
720 @raise ValueError: Invalid or non-finite B{C{xs}} or B{C{ys}}.
721 '''
722 n = self._n
723 if xs and ys:
724 S = self._S
725 X = self._X
726 Y = self._Y
727 for x, y in _zip(_Xs(xs=xs), _Xs(ys=ys)): # PYCHOK strict=True
728 n1 = n
729 n += 1
730 if n1 > 0:
731 S += (X._M1 - x) * (Y._M1 - y) * (n1 / n)
732 X += x
733 Y += y
734 self._n = n
735 return _sampled(n, sample)
737 def fadd_(self, *x_ys, **sample):
738 '''Accumulate and return the current count.
740 @arg x_ys: Individual, alternating C{x, y, x, y, ...} values
741 (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
743 @see: Method C{Flinear.fadd} for further details.
744 '''
745 if isodd(len(x_ys)):
746 t = _SPACE_(_odd_, typename(len))
747 raise _ValueError(t, len(x_ys))
748 return self.fadd(x_ys[0::2], x_ys[1::2], **sample)
750 def fcorrelation(self, **sample):
751 '''Return the current, running (sample) correlation (C{float}).
753 @kwarg sample: Use C{B{sample}=True} for the I{sample} correlation
754 instead of the I{population} correlation (C{bool}).
755 '''
756 return float(self._Correlation(**sample))
758 def fintercept(self, **sample):
759 '''Return the current, running (sample) intercept (C{float}).
761 @kwarg sample: Use C{B{sample}=True} for the I{sample} intercept
762 instead of the I{population} intercept (C{bool}).
763 '''
764 return float(self._Intercept(**sample))
766 def fslope(self, **sample):
767 '''Return the current, running (sample) slope (C{float}).
769 @kwarg sample: Use C{B{sample}=True} for the I{sample} slope
770 instead of the I{population} slope (C{bool}).
771 '''
772 return float(self._Slope(**sample))
774 def _Intercept(self, **sample):
775 '''(INTERNAL) Return the current (sample) intercept as L{Fsum}.
776 '''
777 return self._Y._M1 - self._X._M1 * self._Slope(**sample)
779 def _Sampled(self, T, sample=False):
780 '''(INTERNAL) Compute the sampled or entire population result.
781 '''
782 T *= _sampled(self._n, sample)
783 return self._S.copy().fdiv(T, raiser=False) if T else T
785 def _Slope(self, **sample):
786 '''(INTERNAL) Return the current (sample) slope as L{Fsum}.
787 '''
788 return self._Sampled(self._X._Variance(**sample), **sample)
790 @property_RO
791 def x(self):
792 '''Get the C{x} accumulator (L{Fcook} or L{Fwelford}).
793 '''
794 return self._X # .copy()
796 @property_RO
797 def y(self):
798 '''Get the C{y} accumulator (L{Fcook} or L{Fwelford}).
799 '''
800 return self._Y # .copy()
803__all__ += _ALL_DOCS(_FstatsBase, _FstatsNamed)
805# **) MIT License
806#
807# Copyright (C) 2021-2025 -- mrJean1 at Gmail -- All Rights Reserved.
808#
809# Permission is hereby granted, free of charge, to any person obtaining a
810# copy of this software and associated documentation files (the "Software"),
811# to deal in the Software without restriction, including without limitation
812# the rights to use, copy, modify, merge, publish, distribute, sublicense,
813# and/or sell copies of the Software, and to permit persons to whom the
814# Software is furnished to do so, subject to the following conditions:
815#
816# The above copyright notice and this permission notice shall be included
817# in all copies or substantial portions of the Software.
818#
819# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
820# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
821# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
822# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
823# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
824# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
825# OTHER DEALINGS IN THE SOFTWARE.