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 and accurate

6 multiplication.

7'''

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

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

10

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

12 _xiterable, _xsubclassof, _zip

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

14 from pygeodesy.errors import _ValueError, _xError, _xkwds_item2, \

15 _xsError

16 from pygeodesy.fmath import Fsqrt, Fmt

17 from pygeodesy.fsums import _2finite, Fsum, _iadd_op_, _isFsum_2Tuple

18 from pygeodesy.interns import _odd_, _SPACE_

19 from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY

20 from pygeodesy.named import _name__, _Named, _NotImplemented, \

21 property_RO

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

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

24

25__all__ = _ALL_LAZY.fstats

26__version__ = '24.10.08'

27

28

29 def _sampled(n, sample):

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

31 '''

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

33

34

35 def _Xs(**name_xs):

36 '''(INTERNAL) Yield all C{xs} as C{float} or L{Fsum}.

37 '''

38 name, xs = _xkwds_item2(name_xs)

39 try:

40 i, x = 0, xs

41 for i, x in enumerate(xs): # don't unravel Fsums

42 yield x._Fsum if _isFsum_2Tuple(x) else x

43 except Exception as X:

44 raise _xsError(X, xs, i, x, name)

45

46

47 class _FstatsNamed(_Named):

48 '''(INTERNAL) Base class.

49 '''

50 _n = 0

51

52 def __add__(self, other):

53 '''Sum of this and an other instance, a C{scalar}, an L{Fsum}

54 or L{Fsum2Tuple}.

55 '''

56 f = self.copy(name=self.__add__.__name__) # PYCHOK expected

57 f += other

58 return f

59

60 def __float__(self): # PYCHOK no cover

61 '''Not implemented.'''

62 return _NotImplemented(self)

63

64 def __int__(self): # PYCHOK no cover

65 '''Not implemented.'''

66 return _NotImplemented(self)

67

68 def __len__(self):

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

70 '''

71 return self._n

72

73 def __neg__(self): # PYCHOK no cover

74 '''Not implemented.'''

75 return _NotImplemented(self)

76

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

78 '''Not implemented.'''

79 return _NotImplemented(self, other)

80

81 def __str__(self):

82 n = self.name

83 n = _SPACE_(self.classname, n) if n else self.classname

84 return Fmt.SQUARE(n, len(self))

85

86 def copy(self, deep=False, **name):

87 '''Copy this instance, C{shallow} or B{C{deep}}.

88

89 @kwarg name: Optional, overriding C{B{name}="copy"} (C{str}).

90

91 @return: The copy instance.

92 '''

93 n = _name__(name, name__=self.copy)

94 f = _Named.copy(self, deep=deep, name=n)

95 return self._copy(f, self) # PYCHOK expected

96

97 fcopy = copy # for backward compatibility

98

99

100 class _FstatsBase(_FstatsNamed):

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

102 '''

103 _Ms = ()

104

105 def _copy(self, d, s):

106 '''(INTERNAL) Copy C{B{d} = B{s}}.

107 '''

108 _xinstanceof(self.__class__, d=d, s=s)

109 d._Ms = tuple(M.copy() for M in s._Ms) # deep=False

110 d._n = s._n

111 return d

112

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

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

115 self._notOverloaded(xs, sample=sample)

116

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

118 '''Accumulate and return the current count.

119

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

121 '''

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

123

124 def fmean(self, xs=None):

125 '''Accumulate and return the current mean.

126

127 @kwarg xs: Iterable of additional values (each C{scalar},

128 an L{Fsum} or L{Fsum2Tuple}).

129

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

131

132 @see: Method C{fadd}.

133 '''

134 return float(self._Mean(xs))

135

136 def fmean_(self, *xs):

137 '''Accumulate and return the current mean.

138

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

140 '''

141 return self.fmean(xs)

142

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

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

145

146 @arg xs: Iterable of additional values (each C{scalar}, an L{Fsum}

147 or L{Fsum2Tuple}).

148 @kwarg sample: Use C{B{sample}=True} for the I{sample} deviation

149 instead of the I{population} deviation (C{bool}).

150

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

152

153 @see: Method C{fadd}.

154 '''

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

156

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

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

159

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

161 '''

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

163

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

165 '''Accumulate and return the current variance.

166

167 @arg xs: Iterable of additional values (each C{scalar}, an L{Fsum}

168 or L{Fsum2Tuple}).

169 @kwarg sample: Use C{B{sample}=True} for the I{sample} variance

170 instead of the I{population} variance (C{bool}).

171

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

173

174 @see: Method C{fadd}.

175 '''

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

177

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

179 '''Accumulate and return the current variance.

180

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

182 '''

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

184

185 def _iadd_other(self, other):

186 '''(INTERNAL) Add one or several values.

187 '''

188 try:

189 if _isFsum_2Tuple(other):

190 self.fadd_(other._Fsum)

191 elif isscalar(other):

192 self.fadd_(_2finite(other))

193 elif _xiterable(other):

194 self.fadd(other)

195 except Exception as X:

196 t = _SPACE_(self, _iadd_op_, repr(other))

197 raise _xError(X, t)

198

199 @property_RO

200 def _M1(self):

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

202 return self._Ms[0]

203

204 @property_RO

205 def _M2(self):

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

207 return self._Ms[1]

208

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

215

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

221

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

227

228

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

234

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

239 '''New L{Fcook} stats accumulator.

240

241 @arg xs: Iterable of additional values (each C{scalar}, an

242 L{Fsum} or L{Fsum2Tuple}).

243 @kwarg name: Optional C{B{name}=NN} (C{str}).

244

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)

252

253 def __iadd__(self, other):

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

255

256 @arg other: An L{Fcook} instance or value or iterable

257 of values (each C{scalar}, an L{Fsum} or

258 L{Fsum2Tuple}).

259

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

261

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

263

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

265

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

267 '''

268 if isinstance(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

275

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)

283

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

290

291 A3 += B3

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

293 A3 += Dn3 * (na - nb)

294

295 A2 += B2

296 A2 += Dn2 * (nab * _n)

297

298 B1n = B1 * nb # if other is self

299 A1 *= na

300 A1 += B1n

301 A1 *= _n

302

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

310

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

312 '''Accumulate and return the current count.

313

314 @arg xs: Iterable of additional values (each C{scalar}, an

315 L{Fsum} or L{Fsum2Tuple}).

316 @kwarg sample: Use C{B{sample}=True} for the I{sample} count

317 instead of the I{population} count (C{bool}).

318

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

320

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

322

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

324

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

326

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

353

354 M3 += T2

355 M3 -= M2 * (Dn * _3_0)

356

357 M2 += T1

358 M1 += Dn

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

360 self._n = n

361 return _sampled(n, sample)

362

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.

366

367 @kwarg xs: Iterable of additional values (each C{scalar}, 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}).

372

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

374

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

376 '''

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

378

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.

382

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

384 '''

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

386

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

388 '''Accumulate and return the current kurtosis.

389

390 @arg xs: Iterable of additional values (each C{scalar}, an L{Fsum}

391 or L{Fsum2Tuple}).

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

395

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

397

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

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

400

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

402 '''

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

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

405

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

407 '''Accumulate and return the current kurtosis.

408

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

410 '''

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

412

413 def fmedian(self, xs=None):

414 '''Accumulate and return the current median.

415

416 @arg xs: Iterable of additional values (each C{scalar}, an L{Fsum} or

417 L{Fsum2Tuple}).

418

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

420

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

427

428 def fmedian_(self, *xs):

429 '''Accumulate and return the current median.

430

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

432 '''

433 return self.fmedian(xs)

434

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

436 '''Accumulate and return the current skewness.

437

438 @arg xs: Iterable of additional values (each C{scalar}, an L{Fsum}

439 or L{Fsum2Tuple}).

440 @kwarg sample: Use C{B{sample}=True} for the I{sample} skewness

441 instead of the I{population} skewness (C{bool}).

442

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

444

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

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

447

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

449 '''

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

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

452

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

454 '''Accumulate and return the current skewness.

455

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

457 '''

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

459

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

469

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

486

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

494

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

508

509 def toFwelford(self, **name):

510 '''Return a L{Fwelford} equivalent.

511

512 @kwarg name: Optional C{B{name}=NN} (C{str}).

513 '''

514 f = Fwelford(name=self._name__(name))

515 f._Ms = self._M1.copy(), self._M2.copy() # deep=False

516 f._n = self._n

517 return f

518

519

520 class Fwelford(_FstatsBase):

521 '''U{Welford<https://WikiPedia.org/wiki/Algorithms_for_calculating_variance>}'s

522 accumulator computing the running mean, (sample) variance and standard deviation.

523

524 @see: U{Cook<https://www.JohnDCook.com/blog/standard_deviation/>} and L{Fcook}.

525 '''

526 def __init__(self, xs=None, **name):

527 '''New L{Fwelford} stats accumulator.

528

529 @arg xs: Iterable of initial values (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}).

530 @kwarg name: Optional C{B{name}=NN} (C{str}).

531

532 @see: Method L{Fwelford.fadd}.

533 '''

534 self._Ms = Fsum(), Fsum() # 1st and 2nd Moment

535 if name:

536 self.name = name

537 if xs:

538 self.fadd(xs)

539

540 def __iadd__(self, other):

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

542

543 @arg other: An L{Fwelford} or L{Fcook} instance or value or an

544 iterable of values (each C{scalar}, an L{Fsum} or

545 L{Fsum2Tuple}).

546

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

548

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

550

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

552

553 @see: Method L{Fwelford.fadd} and U{Parallel algorithm<https//

554 WikiPedia.org/wiki/Algorithms_for_calculating_variance>}.

555 '''

556 if isinstance(other, Fwelford):

557 nb = len(other)

558 if nb > 0:

559 na = len(self)

560 if na > 0:

561 M, S = self._Ms

562 M_, S_ = other._Ms

563

564 n = na + nb

565 _n = _1_0 / n

566

567 D = M_ - M

568 D *= D # D**2

569 D *= na * nb * _n

570 S += D

571 S += S_

572

573 Mn = M_ * nb # if other is self

574 M *= na

575 M += Mn

576 M *= _n

577

578# self._Ms = M, S

579 self._n = n

580 else:

581 self._copy(self, other)

582

583 elif isinstance(other, Fcook):

584 self += other.toFwelford()

585 else:

586 self._iadd_other(other)

587 return self

588

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

590 '''Accumulate and return the current count.

591

592 @arg xs: Iterable of additional values (each C{scalar}, an

593 L{Fsum} or L{Fsum2Tuple}).

594 @kwarg sample: Use C{B{sample}=True} for the I{sample} count

595 instead of the I{population} count (C{bool}).

596

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

598

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

600

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

602

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

604 '''

605 n = self._n

606 if xs:

607 M, S = self._Ms

608 for x in _Xs(xs=xs): # PYCHOK yield

609 n += 1

610 D = x - M

611 M += D / n

612 D *= x - M

613 S += D

614# self._Ms = M, S

615 self._n = n

616 return _sampled(n, sample)

617

618

619 class Flinear(_FstatsNamed):

620 '''U{Cook<https://www.JohnDCook.com/blog/running_regression>}'s

621 C{RunningRegression} computing the running slope, intercept

622 and correlation of a linear regression.

623 '''

624 def __init__(self, xs=None, ys=None, Fstats=Fwelford, **name):

625 '''New L{Flinear} regression accumulator.

626

627 @kwarg xs: Iterable of initial C{x} values (each C{scalar},

628 an L{Fsum} or L{Fsum2Tuple}).

629 @kwarg ys: Iterable of initial C{y} values (each C{scalar},

630 an L{Fsum} or L{Fsum2Tuple}).

631 @kwarg Fstats: Class for C{xs} and C{ys} values (L{Fcook} or

632 L{Fwelford}).

633 @kwarg name: Optional C{B{name}=NN} (C{str}).

634

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

636

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

638 '''

639 _xsubclassof(Fcook, Fwelford, Fstats=Fstats)

640 if name:

641 self.name = name

642

643 self._S = Fsum(name=name)

644 self._X = Fstats(name=name)

645 self._Y = Fstats(name=name)

646 if xs and ys:

647 self.fadd(xs, ys)

648

649 def __iadd__(self, other):

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

651

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

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

654

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

656

657 @raise TypeError: Invalid B{C{other}} or the B{C{other}}

658 and these C{x} and C{y} accumulators

659 are not compatible.

660

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

662

663 @see: Method L{Flinear.fadd_}.

664 '''

665 if isinstance(other, Flinear):

666 if len(other) > 0:

667 if len(self) > 0:

668 n = other._n

669 D = (other._X._M1 - self._X._M1) * \

670 (other._Y._M1 - self._Y._M1) * \

671 (n * self._n / (self._n + n))

672 self._S += other._S + D

673 self._X += other._X

674 self._Y += other._Y

675 self._n += n

676 else:

677 self._copy(self, other)

678 else:

679 try:

680 if _xiterable(other):

681 self.fadd_(*other)

682 except Exception as X:

683 op = _SPACE_(self, _iadd_op_, repr(other))

684 raise _xError(X, op)

685 return self

686

687 def _copy(self, d, s):

688 '''(INTERNAL) Copy C{B{d} = B{s}}.

689 '''

690 _xinstanceof(Flinear, d=d, s=s)

691 d._S = s._S.copy(deep=False)

692 d._X = s._X.copy(deep=False)

693 d._Y = s._Y.copy(deep=False)

694 d._n = s._n

695 return d

696

697 def _Correlation(self, **sample):

698 '''(INTERNAL) Return the current (sample) correlation as L{Fsum}.

699 '''

700 return self._Sampled(self._X._Stdev(**sample) *

701 self._Y._Stdev(**sample), **sample)

702

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

704 '''Accumulate and return the current count.

705

706 @arg xs: Iterable of additional C{x} values (each C{scalar},

707 an L{Fsum} or L{Fsum2Tuple}).

708 @arg ys: Iterable of additional C{y} values (each C{scalar},

709 an L{Fsum} or L{Fsum2Tuple}).

710 @kwarg sample: Use C{B{sample}=True} for the I{sample} count

711 instead of the I{population} count (C{bool}).

712

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

714

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

716

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

718

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

720 '''

721 n = self._n

722 if xs and ys:

723 S = self._S

724 X = self._X

725 Y = self._Y

726 for x, y in _zip(_Xs(xs=xs), _Xs(ys=ys)): # PYCHOK strict=True

727 n1 = n

728 n += 1

729 if n1 > 0:

730 S += (X._M1 - x) * (Y._M1 - y) * (n1 / n)

731 X += x

732 Y += y

733 self._n = n

734 return _sampled(n, sample)

735

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

737 '''Accumulate and return the current count.

738

739 @arg x_ys: Individual, alternating C{x, y, x, y, ...} values

740 (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}).

741

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)

748

749 def fcorrelation(self, **sample):

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

751

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

756

757 def fintercept(self, **sample):

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

759

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

764

765 def fslope(self, **sample):

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

767

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

772

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)

777

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

783

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)

788

789 @property_RO

790 def x(self):

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

792 '''

793 return self._X # .copy()

794

795 @property_RO

796 def y(self):

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

798 '''

799 return self._Y # .copy()

800

801

802__all__ += _ALL_DOCS(_FstatsBase, _FstatsNamed)

803

804# **) MIT License

805#

806# Copyright (C) 2021-2025 -- 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.