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 isodd, islistuple, _xinstanceof, \

11 _xsubclassof, _zip

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

13# from pygeodesy.errors import _xError # from .constants

14 from pygeodesy.fmath import hypot2, sqrt

15 from pygeodesy.fsums import _2float, Fsum, Fmt

16 from pygeodesy.interns import NN, _iadd_op_, _invalid_, _other_, _SPACE_

17 from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY

18 from pygeodesy.named import _Named, _NotImplemented, notOverloaded, \

19 property_RO

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

21# from pygeodesy.streprs import Fmt # from .fsums

22

23# from math import sqrt # from .fmath

24

25__all__ = _ALL_LAZY.fstats

26__version__ = '23.09.08'

27

28_Floats = Fsum, float

29_Scalar = _Floats + (int,) # XXX basics._Ints is ABCMeta in Python 2

30 try:

31 _Scalar += (long,)

32 except NameError: # Python 3+

33 pass

34

35

36 def _2Floats(xs, ys=False):

37 '''(INTERNAL) Yield each value as C{float} or L{Fsum}.

38 '''

39 if ys:

40 def _2f(i, x):

41 return _2float(index=i, ys=x)

42 else:

43 def _2f(i, x): # PYCHOK redef

44 return _2float(index=i, xs=x)

45

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

47 yield x if isinstance(x, _Floats) else _2f(i, x)

48

49

50 def _sampled(n, sample):

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

52 '''

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

54

55

56 class _FstatsNamed(_Named):

57 '''(INTERNAL) Base class.

58 '''

59 _n = 0

60

61 def __add__(self, other):

62 '''Sum of this and a scalar, an L{Fsum} or an other instance.

63 '''

64 f = self.fcopy(name=self.__add__.__name__) # PYCHOK expected

65 f += other

66 return f

67

68 def __float__(self): # PYCHOK no cover

69 '''Not implemented.'''

70 return _NotImplemented(self)

71

72 def __int__(self): # PYCHOK no cover

73 '''Not implemented.'''

74 return _NotImplemented(self)

75

76 def __len__(self):

77 '''Return the I{total} number of accumulated values (C{int}).

78 '''

79 return self._n

80

81 def __neg__(self): # PYCHOK no cover

82 '''Not implemented.'''

83 return _NotImplemented(self)

84

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

86 '''Not implemented.'''

87 return _NotImplemented(self, other)

88

89 def __str__(self):

90 return Fmt.SQUARE(self.named3, len(self))

91

92 def fcopy(self, deep=False, name=NN):

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

94 '''

95 n = name or self.fcopy.__name__

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

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

98

99 copy = fcopy

100

101

102 class _FstatsBase(_FstatsNamed):

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

104 '''

105 _Ms = ()

106

107 def _copy(self, c, s):

108 '''(INTERNAL) Copy C{B{c} = B{s}}.

109 '''

110 _xinstanceof(self.__class__, c=c, s=s)

111 c._Ms = tuple(M.fcopy() for M in s._Ms) # deep=False

112 c._n = s._n

113 return c

114

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

116 '''(INTERNAL) I{Must be overloaded}, see function C{notOverloaded}.

117 '''

118 notOverloaded(self, xs, sample=sample)

119

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

121 '''Accumulate and return the current count.

122

123 @see: Method C{fadd}.

124 '''

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

126

127 def fmean(self, xs=None):

128 '''Accumulate and return the current mean.

129

130 @kwarg xs: Iterable with additional values (C{Scalar}s).

131

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

133

134 @see: Method C{fadd}.

135 '''

136 if xs:

137 self.fadd(xs)

138 return self._M1.fsum()

139

140 def fmean_(self, *xs):

141 '''Accumulate and return the current mean.

142

143 @see: Method C{fmean}.

144 '''

145 return self.fmean(xs)

146

147 def fstdev(self, xs=None, sample=False):

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

149

150 @kwarg xs: Iterable with additional values (C{Scalar}).

151 @kwarg sample: Return the I{sample} instead of the entire

152 I{population} standard deviation (C{bool}).

153

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

155

156 @see: Method C{fadd}.

157 '''

158 v = self.fvariance(xs, sample=sample)

159 return sqrt(v) if v > 0 else _0_0

160

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

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

163

164 @see: Method C{fstdev}.

165 '''

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

167

168 def fvariance(self, xs=None, sample=False):

169 '''Accumulate and return the current variance.

170

171 @kwarg xs: Iterable with additional values (C{Scalar}s).

172 @kwarg sample: Return the I{sample} instead of the entire

173 I{population} variance (C{bool}).

174

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

176

177 @see: Method C{fadd}.

178 '''

179 n = self.fadd(xs, sample=sample)

180 return float(self._M2 / float(n)) if n > 0 else _0_0

181

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

183 '''Accumulate and return the current variance.

184

185 @see: Method C{fvariance}.

186 '''

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

188

189 def _iadd_other(self, other):

190 '''(INTERNAL) Add Scalar or Scalars.

191 '''

192 if isinstance(other, _Scalar):

193 self.fadd_(other)

194 else:

195 try:

196 if not islistuple(other):

197 raise TypeError(_SPACE_(_invalid_, _other_))

198 self.fadd(other)

199 except Exception as x:

200 raise _xError(x, _SPACE_(self, _iadd_op_, repr(other)))

201

202 @property_RO

203 def _M1(self):

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

205 return self._Ms[0]

206

207 @property_RO

208 def _M2(self):

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

210 return self._Ms[1]

211

212

213 class Fcook(_FstatsBase):

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

215 C{RunningStats} computing the running mean, median and

216 (sample) kurtosis, skewness, variance, standard deviation

217 and Jarque-Bera normality.

218

219 @see: L{Fwelford} and U{Higher-order statistics<https://

220 WikiPedia.org/wiki/Algorithms_for_calculating_variance>}.

221 '''

222 def __init__(self, xs=None, name=NN):

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

224

225 @kwarg xs: Iterable with initial values (C{Scalar}s).

226 @kwarg name: Optional name (C{str}).

227

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

229 '''

230 self._Ms = tuple(Fsum() for _ in range(4)) # 1st, 2nd ... Moment

231 if name:

232 self.name = name

233 if xs:

234 self.fadd(xs)

235

236 def __iadd__(self, other):

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

238

239 @arg other: An L{Fcook} instance or C{Scalar}s, meaning

240 one or more C{scalar} or L{Fsum} instances.

241

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

243

244 @raise TypeError: Invalid B{C{other}} type.

245

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

247

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

249 '''

250 if isinstance(other, Fcook):

251 nb = len(other)

252 if nb > 0:

253 na = len(self)

254 if na > 0:

255 A1, A2, A3, A4 = self._Ms

256 B1, B2, B3, B4 = other._Ms

257

258 n = na + nb

259 n_ = float(n)

260 D = A1 - B1 # b1 - a1

261 Dn = D / n_

262 Dn2 = Dn**2 # d**2 / n**2

263 nab = na * nb

264 Dn3 = Dn2 * (D * nab)

265

266 na2 = na**2

267 nb2 = nb**2

268 A4 += B4

269 A4 += (B3 * na - (A3 * nb)) * (Dn * _4_0)

270 A4 += (B2 * na2 + (A2 * nb2)) * (Dn2 * _6_0)

271 A4 += (Dn * Dn3) * (na2 - nab + nb2) # d**4 / n**3

272

273 A3 += B3

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

275 A3 += Dn3 * (na - nb)

276

277 A2 += B2

278 A2 += Dn2 * (nab / n_)

279

280 B1n = B1 * nb # if other is self

281 A1 *= na

282 A1 += B1n

283 A1 *= 1 / n_ # /= chokes PyChecker

284

285# self._Ms = A1, A2, A3, A4

286 self._n = n

287 else:

288 self._copy(self, other)

289 else:

290 self._iadd_other(other)

291 return self

292

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

294 '''Accumulate and return the current count.

295

296 @arg xs: Iterable with additional values (C{Scalar}s,

297 meaning C{scalar} or L{Fsum} instances).

298 @kwarg sample: Return the I{sample} instead of the entire

299 I{population} count (C{bool}).

300

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

302

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

304

305 @raise TypeError: Non-scalar B{C{xs}} value.

306

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

308

309 @see: U{online_kurtosis<https://WikiPedia.org/wiki/

310 Algorithms_for_calculating_variance>}.

311 '''

312 n = self._n

313 if xs:

314 M1, M2, M3, M4 = self._Ms

315 for x in _2Floats(xs):

316 n1 = n

317 n += 1

318 D = x - M1

319 Dn = D / n

320 if Dn:

321 Dn2 = Dn**2

322 if n1 > 1:

323 T1 = D * (Dn * n1)

324 T2 = T1 * (Dn * (n1 - 1))

325 T3 = T1 * (Dn2 * (n**2 - 3 * n1))

326 elif n1 > 0: # n1 == 1, n == 2

327 T1 = D * Dn

328 T2 = _0_0

329 T3 = T1 * Dn2

330 else:

331 T1 = T2 = T3 = _0_0

332 M4 += T3

333 M4 -= M3 * (Dn * _4_0)

334 M4 += M2 * (Dn2 * _6_0)

335

336 M3 += T2

337 M3 -= M2 * (Dn * _3_0)

338

339 M2 += T1

340 M1 += Dn

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

342 self._n = n

343 return _sampled(n, sample)

344

345 def fjb(self, xs=None, sample=True, excess=True):

346 '''Accumulate and compute the current U{Jarque-Bera

347 <https://WikiPedia.org/wiki/Jarque–Bera_test>} normality.

348

349 @kwarg xs: Iterable with additional values (C{Scalar}s).

350 @kwarg sample: Return the I{sample} normality (C{bool}), default.

351 @kwarg excess: Return the I{excess} kurtosis (C{bool}), default.

352

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

354

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

356 '''

357 n = self.fadd(xs, sample=sample)

358 k = self.fkurtosis(sample=sample, excess=excess) / _2_0

359 s = self.fskewness(sample=sample)

360 return n * hypot2(k, s) / _6_0

361

362 def fjb_(self, *xs, **sample_excess):

363 '''Accumulate and compute the current U{Jarque-Bera

364 <https://WikiPedia.org/wiki/Jarque–Bera_test>} normality.

365

366 @see: Method L{Fcook.fjb}.

367 '''

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

369

370 def fkurtosis(self, xs=None, sample=False, excess=True):

371 '''Accumulate and return the current kurtosis.

372

373 @kwarg xs: Iterable with additional values (C{Scalar}s).

374 @kwarg sample: Return the I{sample} instead of the entire

375 I{population} kurtosis (C{bool}).

376 @kwarg excess: Return the I{excess} kurtosis (C{bool}), default.

377

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

379

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

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

382

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

384 '''

385 k, n = _0_0, self.fadd(xs, sample=sample)

386 if n > 0:

387 _, M2, _, M4 = self._Ms

388 m2 = float(M2 * M2)

389 if m2:

390 K, x = (M4 * (n / m2)), _3_0

391 if sample and 2 < n < len(self):

392 d = float((n - 1) * (n - 2))

393 K *= (n + 1) * (n + 2) / d

394 x *= n**2 / d

395 if excess:

396 K -= x

397 k = K.fsum()

398 return k

399

400 def fkurtosis_(self, *xs, **sample_excess):

401 '''Accumulate and return the current kurtosis.

402

403 @see: Method L{Fcook.fkurtosis}.

404 '''

405 return self.fkurtosis(xs, **sample_excess)

406

407 def fmedian(self, xs=None):

408 '''Accumulate and return the current median.

409

410 @kwarg xs: Iterable with additional values (C{Scalar}s).

411

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

413

414 @see: U{Pearson's Skewness Coefficients<https://MathWorld.Wolfram.com/

415 PearsonsSkewnessCoefficients.html>}, U{Skewness & Kurtosis Simplified

416 https://TowardsDataScience.com/skewness-kurtosis-simplified-1338e094fc85>}

417 and method L{Fcook.fadd}.

418 '''

419 # skewness = 3 * (mean - median) / stdev, i.e.

420 # median = mean - skewness * stdef / 3

421 m = float(self._M1) if xs is None else self.fmean(xs)

422 return m - self.fskewness() * self.fstdev() / _3_0

423

424 def fmedian_(self, *xs):

425 '''Accumulate and return the current median.

426

427 @see: Method L{Fcook.fmedian}.

428 '''

429 return self.fmedian(xs)

430

431 def fskewness(self, xs=None, sample=False):

432 '''Accumulate and return the current skewness.

433

434 @kwarg xs: Iterable with additional values (C{Scalar}s).

435 @kwarg sample: Return the I{sample} instead of the entire

436 I{population} skewness (C{bool}).

437

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

439

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

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

442

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

444 '''

445 s, n = _0_0, self.fadd(xs, sample=sample)

446 if n > 0:

447 _, M2, M3, _ = self._Ms

448 m = float(M2**3)

449 if m > 0:

450 S = M3 * sqrt(float(n) / m)

451 if sample and 1 < n < len(self):

452 S *= (n + 1) / float(n - 1)

453 s = S.fsum()

454 return s

455

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

457 '''Accumulate and return the current skewness.

458

459 @see: Method L{Fcook.fskewness}.

460 '''

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

462

463 def toFwelford(self, name=NN):

464 '''Return an L{Fwelford} equivalent.

465 '''

466 f = Fwelford(name=name or self.name)

467 f._Ms = self._M1.fcopy(), self._M2.fcopy() # deep=False

468 f._n = self._n

469 return f

470

471

472 class Fwelford(_FstatsBase):

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

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

475

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

477 '''

478 def __init__(self, xs=None, name=NN):

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

480

481 @kwarg xs: Iterable with initial values (C{Scalar}s).

482 @kwarg name: Optional name (C{str}).

483

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

485 '''

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

487 if name:

488 self.name = name

489 if xs:

490 self.fadd(xs)

491

492 def __iadd__(self, other):

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

494

495 @arg other: An L{Fwelford} or L{Fcook} instance or C{Scalar}s,

496 meaning one or more C{scalar} or L{Fsum} instances.

497

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

499

500 @raise TypeError: Invalid B{C{other}} type.

501

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

503

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

505 WikiPedia.org/wiki/Algorithms_for_calculating_variance>}.

506 '''

507 if isinstance(other, Fwelford):

508 nb = len(other)

509 if nb > 0:

510 na = len(self)

511 if na > 0:

512 M, S = self._Ms

513 M_, S_ = other._Ms

514

515 n = na + nb

516 n_ = float(n)

517

518 D = M_ - M

519 D *= D # D**2

520 D *= na * nb / n_

521 S += D

522 S += S_

523

524 Mn = M_ * nb # if other is self

525 M *= na

526 M += Mn

527 M *= 1 / n_ # /= chokes PyChecker

528

529# self._Ms = M, S

530 self._n = n

531 else:

532 self._copy(self, other)

533

534 elif isinstance(other, Fcook):

535 self += other.toFwelford()

536 else:

537 self._iadd_other(other)

538 return self

539

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

541 '''Accumulate and return the current count.

542

543 @arg xs: Iterable with additional values (C{Scalar}s,

544 meaning C{scalar} or L{Fsum} instances).

545 @kwarg sample: Return the I{sample} instead of the entire

546 I{population} count (C{bool}).

547

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

549

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

551

552 @raise TypeError: Non-scalar B{C{xs}} value.

553

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

555 '''

556 n = self._n

557 if xs:

558 M, S = self._Ms

559 for x in _2Floats(xs):

560 n += 1

561 D = x - M

562 M += D / n

563 D *= x - M

564 S += D

565# self._Ms = M, S

566 self._n = n

567 return _sampled(n, sample)

568

569

570 class Flinear(_FstatsNamed):

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

572 C{RunningRegression} computing the running slope, intercept

573 and correlation of a linear regression.

574 '''

575 def __init__(self, xs=None, ys=None, Fstats=Fwelford, name=NN):

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

577

578 @kwarg xs: Iterable with initial C{x} values (C{Scalar}s).

579 @kwarg ys: Iterable with initial C{y} values (C{Scalar}s).

580 @kwarg Fstats: Stats class for C{x} and C{y} values (L{Fcook}

581 or L{Fwelford}).

582 @kwarg name: Optional name (C{str}).

583

584 @raise TypeError: Invalid B{C{Fs}}, not L{Fcook} or

585 L{Fwelford}.

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

587 '''

588 _xsubclassof(Fcook, Fwelford, Fstats=Fstats)

589 if name:

590 self.name = name

591

592 self._S = Fsum(name=name)

593 self._X = Fstats(name=name)

594 self._Y = Fstats(name=name)

595 if xs and ys:

596 self.fadd(xs, ys)

597

598 def __iadd__(self, other):

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

600

601 @arg other: An L{Flinear} instance or C{Scalar} pairs,

602 meaning C{scalar} or L{Fsum} instances.

603

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

605

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

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

608 are not compatible.

609

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

611

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

613 '''

614 if isinstance(other, Flinear):

615 if len(other) > 0:

616 if len(self) > 0:

617 n = other._n

618 S = other._S

619 X = other._X

620 Y = other._Y

621 D = (X._M1 - self._X._M1) * \

622 (Y._M1 - self._Y._M1) * \

623 (n * self._n / float(n + self._n))

624 self._n += n

625 self._S += S + D

626 self._X += X

627 self._Y += Y

628 else:

629 self._copy(self, other)

630 else:

631 try:

632 if not islistuple(other):

633 raise TypeError(_SPACE_(_invalid_, _other_))

634 elif isodd(len(other)):

635 raise ValueError(Fmt.PAREN(isodd=Fmt.PAREN(len=_other_)))

636 self.fadd_(*other)

637 except Exception as x:

638 raise _xError(x, _SPACE_(self, _iadd_op_, repr(other)))

639 return self

640

641 def _copy(self, c, s):

642 '''(INTERNAL) Copy C{B{c} = B{s}}.

643 '''

644 _xinstanceof(Flinear, c=c, s=s)

645 c._n = s._n

646 c._S = s._S.fcopy(deep=False)

647 c._X = s._X.fcopy(deep=False)

648 c._Y = s._Y.fcopy(deep=False)

649 return c

650

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

652 '''Accumulate and return the current count.

653

654 @arg xs: Iterable with additional C{x} values (C{Scalar}s),

655 meaning C{scalar} or L{Fsum} instances).

656 @arg ys: Iterable with additional C{y} values (C{Scalar}s,

657 meaning C{scalar} or L{Fsum} instances).

658 @kwarg sample: Return the I{sample} instead of the entire

659 I{population} count (C{bool}).

660

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

662

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

664

665 @raise TypeError: Non-scalar B{C{xs}} or B{C{ys}} value.

666

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

668 '''

669 n = self._n

670 if xs and ys:

671 S = self._S

672 X = self._X

673 Y = self._Y

674 for x, y in _zip(_2Floats(xs), _2Floats(ys, ys=True)): # strict=True

675 n1 = n

676 n += 1

677 if n1 > 0:

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

679 X += x

680 Y += y

681 self._n = n

682 return _sampled(n, sample)

683

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

685 '''Accumulate and return the current count.

686

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

688 positional values (C{Scalar}s).

689

690 @see: Method C{Flinear.fadd}.

691 '''

692 return self.fadd(x_ys[0::2], x_ys[1::2], **sample)

693

694 def fcorrelation(self, sample=False):

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

696

697 @kwarg sample: Return the I{sample} instead of the entire

698 I{population} correlation (C{bool}).

699 '''

700 return self._sampled(self.x.fstdev(sample=sample) *

701 self.y.fstdev(sample=sample), sample)

702

703 def fintercept(self, sample=False):

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

705

706 @kwarg sample: Return the I{sample} instead of the entire

707 I{population} intercept (C{bool}).

708 '''

709 return float(self.y._M1 -

710 (self.x._M1 * self.fslope(sample=sample)))

711

712 def fslope(self, sample=False):

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

714

715 @kwarg sample: Return the I{sample} instead of the entire

716 I{population} slope (C{bool}).

717 '''

718 return self._sampled(self.x.fvariance(sample=sample), sample)

719

720 def _sampled(self, t, sample):

721 '''(INTERNAL) Compute the sampled or entire population result.

722 '''

723 t *= float(_sampled(self._n, sample))

724 return float(self._S / t) if t else _0_0

725

726 @property_RO

727 def x(self):

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

729 '''

730 return self._X

731

732 @property_RO

733 def y(self):

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

735 '''

736 return self._Y

737

738

739__all__ += _ALL_DOCS(_FstatsBase, _FstatsNamed)

740

741# **) MIT License

742#

743# Copyright (C) 2021-2023 -- mrJean1 at Gmail -- All Rights Reserved.

744#

745# Permission is hereby granted, free of charge, to any person obtaining a

746# copy of this software and associated documentation files (the "Software"),

747# to deal in the Software without restriction, including without limitation

748# the rights to use, copy, modify, merge, publish, distribute, sublicense,

749# and/or sell copies of the Software, and to permit persons to whom the

750# Software is furnished to do so, subject to the following conditions:

751#

752# The above copyright notice and this permission notice shall be included

753# in all copies or substantial portions of the Software.

754#

755# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS

756# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

757# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL

758# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR

759# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,

760# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR

761# OTHER DEALINGS IN THE SOFTWARE.