1

2# -*- coding: utf-8 -*-

3

4 u'''Classes for I{running} statistics and regressions based on

5 L{pygeodesy.Fsum}, precision floating point summation.

6'''

7# make sure int/int division yields float quotient, see .basics

8 from __future__ import division as _; del _ # PYCHOK semicolon

9

10 from pygeodesy.basics import isscalar, isodd, _xinstanceof, \

11 _xiterable, _xsubclassof, _zip

12 from pygeodesy.constants import _0_0, _1_0, _2_0, _3_0, _4_0, _6_0

13 from pygeodesy.errors import _AssertionError, _ValueError, _xError

14 from pygeodesy.fmath import Fsqrt, Fmt

15 from pygeodesy.fsums import _2finite, Fsum, _iadd_op_, _isFsumTuple

16 from pygeodesy.interns import NN, _odd_, _SPACE_

17 from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY

18 from pygeodesy.named import _Named, _NotImplemented, property_RO

19# from pygeodesy.props import property_RO # from .named

20# from pygeodesy.streprs import Fmt # from .fmath

21

22__all__ = _ALL_LAZY.fstats

23__version__ = '24.05.10'

24

25

26 def _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)

33

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)

41

42

43 def _sampled(n, sample):

44 '''(INTERNAL) Return the sample or the entire count.

45 '''

46 return (n - 1) if sample and n > 0 else n

47

48

49 class _FstatsNamed(_Named):

50 '''(INTERNAL) Base class.

51 '''

52 _n = 0

53

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

62

63 def __float__(self): # PYCHOK no cover

64 '''Not implemented.'''

65 return _NotImplemented(self)

66

67 def __int__(self): # PYCHOK no cover

68 '''Not implemented.'''

69 return _NotImplemented(self)

70

71 def __len__(self):

72 '''Return the I{total} number of accumulated C{Scalars} (C{int}).

73 '''

74 return self._n

75

76 def __neg__(self): # PYCHOK no cover

77 '''Not implemented.'''

78 return _NotImplemented(self)

79

80 def __radd__(self, other): # PYCHOK no cover

81 '''Not implemented.'''

82 return _NotImplemented(self, other)

83

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))

88

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

95

96 fcopy = copy # for backward compatibility

97

98

99 class _FstatsBase(_FstatsNamed):

100 '''(INTERNAL) Base running stats class.

101 '''

102 _Ms = ()

103

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

111

112 def fadd(self, xs, sample=False): # PYCHOK no cover

113 '''I{Must be overloaded}.'''

114 self._notOverloaded(xs, sample=sample)

115

116 def fadd_(self, *xs, **sample):

117 '''Accumulate and return the current count.

118

119 @see: Method C{fadd} for further details.

120 '''

121 return self.fadd(xs, **sample)

122

123 def fmean(self, xs=None):

124 '''Accumulate and return the current mean.

125

126 @kwarg xs: Iterable of additional values (each C{scalar} or

127 an L{Fsum} or L{Fsum2Tuple} instance).

128

129 @return: Current, running mean (C{float}).

130

131 @see: Method C{fadd}.

132 '''

133 return float(self._Mean(xs))

134

135 def fmean_(self, *xs):

136 '''Accumulate and return the current mean.

137

138 @see: Method C{fmean} for further details.

139 '''

140 return self.fmean(xs)

141

142 def fstdev(self, xs=None, **sample):

143 '''Accumulate and return the current standard deviation.

144

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}).

149

150 @return: Current, running (sample) standard deviation (C{float}).

151

152 @see: Method C{fadd}.

153 '''

154 return float(self._Stdev(xs, **sample))

155

156 def fstdev_(self, *xs, **sample):

157 '''Accumulate and return the current standard deviation.

158

159 @see: Method C{fstdev} for further details.

160 '''

161 return self.fstdev(xs, **sample)

162

163 def fvariance(self, xs=None, **sample):

164 '''Accumulate and return the current variance.

165

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}).

170

171 @return: Current, running (sample) variance (C{float}).

172

173 @see: Method C{fadd}.

174 '''

175 return float(self._Variance(xs, **sample))

176

177 def fvariance_(self, *xs, **sample):

178 '''Accumulate and return the current variance.

179

180 @see: Method C{fvariance} for further details.

181 '''

182 return self.fvariance(xs, **sample)

183

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)

197

198 @property_RO

199 def _M1(self):

200 '''(INTERNAL) get the 1st Moment accumulator.'''

201 return self._Ms[0]

202

203 @property_RO

204 def _M2(self):

205 '''(INTERNAL) get the 2nd Moment accumulator.'''

206 return self._Ms[1]

207

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()

214

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

220

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

226

227

228 class 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.

233

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.

239

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}).

243

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)

251

252 def __iadd__(self, other):

253 '''Add B{C{other}} to this L{Fcook} instance.

254

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).

258

259 @return: This instance, updated (L{Fcook}).

260

261 @raise TypeError: Invalid B{C{other}}.

262

263 @raise ValueError: Invalid or non-finite B{C{other}}.

264

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

274

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)

282

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

289

290 A3 += B3

291 A3 += (A2 * na - (B2 * nb)) * (Dn * _3_0)

292 A3 += Dn3 * (na - nb)

293

294 A2 += B2

295 A2 += Dn2 * (nab * _n)

296

297 B1n = B1 * nb # if other is self

298 A1 *= na

299 A1 += B1n

300 A1 *= _n

301

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

309

310 def fadd(self, xs, sample=False):

311 '''Accumulate and return the current count.

312

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}).

317

318 @return: Current, running (sample) count (C{int}).

319

320 @raise OverflowError: Partial C{2sum} overflow.

321

322 @raise TypeError: Invalid B{C{xs}}.

323

324 @raise ValueError: Invalid or non-finite B{C{xs}}.

325

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)

352

353 M3 += T2

354 M3 -= M2 * (Dn * _3_0)

355

356 M2 += T1

357 M1 += Dn

358# self._Ms = M1, M2, M3, M4

359 self._n = n

360 return _sampled(n, sample)

361

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.

365

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}).

371

372 @return: Current, running (sample) Jarque-Bera normality (C{float}).

373

374 @see: Method L{Fcook.fadd}.

375 '''

376 return float(self._JarqueBera(xs, excess, sample=sample))

377

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.

381

382 @see: Method L{Fcook.fjb} for further details.

383 '''

384 return self.fjb(xs, **sample_excess)

385

386 def fkurtosis(self, xs=None, excess=True, **sample):

387 '''Accumulate and return the current kurtosis.

388

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}).

394

395 @return: Current, running (sample) kurtosis or I{excess} kurtosis (C{float}).

396

397 @see: U{Kurtosis Formula<https://www.Macroption.com/kurtosis-formula>}

398 and U{Mantalos<https://www.ResearchGate.net/publication/227440210>}.

399

400 @see: Method L{Fcook.fadd}.

401 '''

402 n = self.fadd(xs, **sample)

403 return float(self._Kurtosis(n, excess, **sample))

404

405 def fkurtosis_(self, *xs, **excess_sample):

406 '''Accumulate and return the current kurtosis.

407

408 @see: Method L{Fcook.fkurtosis} for further details.

409 '''

410 return self.fkurtosis(xs, **excess_sample)

411

412 def fmedian(self, xs=None):

413 '''Accumulate and return the current median.

414

415 @arg xs: Iterable of additional values (each C{scalar} or an

416 L{Fsum} or L{Fsum2Tuple} instance).

417

418 @return: Current, running median (C{float}).

419

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))

426

427 def fmedian_(self, *xs):

428 '''Accumulate and return the current median.

429

430 @see: Method L{Fcook.fmedian} for further details.

431 '''

432 return self.fmedian(xs)

433

434 def fskewness(self, xs=None, **sample):

435 '''Accumulate and return the current skewness.

436

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}).

441

442 @return: Current, running (sample) skewness (C{float}).

443

444 @see: U{Skewness Formula<https://www.Macroption.com/skewness-formula/>}

445 and U{Mantalos<https://www.ResearchGate.net/publication/227440210>}.

446

447 @see: Method L{Fcook.fadd}.

448 '''

449 n = self.fadd(xs, **sample)

450 return float(self._Skewness(n, **sample))

451

452 def fskewness_(self, *xs, **sample):

453 '''Accumulate and return the current skewness.

454

455 @see: Method L{Fcook.fskewness} for further details.

456 '''

457 return self.fskewness(xs, **sample)

458

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

468

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

485

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

493

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

507

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

515

516

517 class 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.

520

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.

525

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}).

529

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)

537

538 def __iadd__(self, other):

539 '''Add B{C{other}} to this L{Fwelford} instance.

540

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).

544

545 @return: This instance, updated (L{Fwelford}).

546

547 @raise TypeError: Invalid B{C{other}}.

548

549 @raise ValueError: Invalid B{C{other}}.

550

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

561

562 n = na + nb

563 _n = _1_0 / n

564

565 D = M_ - M

566 D *= D # D**2

567 D *= na * nb * _n

568 S += D

569 S += S_

570

571 Mn = M_ * nb # if other is self

572 M *= na

573 M += Mn

574 M *= _n

575

576# self._Ms = M, S

577 self._n = n

578 else:

579 self._copy(self, other)

580

581 elif isinstance(other, Fcook):

582 self += other.toFwelford()

583 else:

584 self._iadd_other(other)

585 return self

586

587 def fadd(self, xs, sample=False):

588 '''Accumulate and return the current count.

589

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}).

594

595 @return: Current, running (sample) count (C{int}).

596

597 @raise OverflowError: Partial C{2sum} overflow.

598

599 @raise TypeError: Invalid B{C{xs}}.

600

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)

615

616

617 class 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.

624

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}).

632

633 @raise TypeError: B{C{Fstats}} not L{Fcook} or L{Fwelford}.

634

635 @see: Method L{Flinear.fadd}.

636 '''

637 _xsubclassof(Fcook, Fwelford, Fstats=Fstats)

638 if name:

639 self.name = name

640

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)

646

647 def __iadd__(self, other):

648 '''Add B{C{other}} to this instance.

649

650 @arg other: An L{Flinear} instance or an iterable of

651 C{x_ys} values, see method C{fadd_}.

652

653 @return: This instance, updated (L{Flinear}).

654

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.

658

659 @raise ValueError: Invalid or odd-length B{C{other}}.

660

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

684

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

694

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)

700

701 def fadd(self, xs, ys, sample=False):

702 '''Accumulate and return the current count.

703

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}).

710

711 @return: Current, running (sample) count (C{int}).

712

713 @raise OverflowError: Partial C{2sum} overflow.

714

715 @raise TypeError: Invalid B{C{xs}} or B{C{ys}}.

716

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)

733

734 def fadd_(self, *x_ys, **sample):

735 '''Accumulate and return the current count.

736

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).

740

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)

747

748 def fcorrelation(self, **sample):

749 '''Return the current, running (sample) correlation (C{float}).

750

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))

755

756 def fintercept(self, **sample):

757 '''Return the current, running (sample) intercept (C{float}).

758

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))

763

764 def fslope(self, **sample):

765 '''Return the current, running (sample) slope (C{float}).

766

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))

771

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)

776

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

782

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)

787

788 @property_RO

789 def x(self):

790 '''Get the C{x} accumulator (L{Fcook} or L{Fwelford}).

791 '''

792 return self._X # .copy()

793

794 @property_RO

795 def y(self):

796 '''Get the C{y} accumulator (L{Fcook} or L{Fwelford}).

797 '''

798 return self._Y # .copy()

799

800

801__all__ += _ALL_DOCS(_FstatsBase, _FstatsNamed)

802

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.