1

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

3

4 u'''Classes for running statistics and regreesions 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, _1_5, _2_0, _3_0, _4_0, _6_0

13 from pygeodesy.errors import _xError

14 from pygeodesy.fmath import hypot2, sqrt

15 from pygeodesy.fsums import _2float, Fmt, Fsum

16 from pygeodesy.interns import NN, _iadd_, _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 # pow from .fmath

24

25__all__ = _ALL_LAZY.fstats

26__version__ = '23.03.29'

27

28_Float = Fsum, float

29_Scalar = _Float + (int,) # XXX basics._Ints is ABCMeta

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 for i, x in enumerate(xs):

40 yield x if isinstance(x, _Float) else (_2float(index=i, ys=x)

41 if ys else _2float(index=i, xs=x))

42

43

44 def _sampled(n, sample):

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

46 '''

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

48

49

50 class _FstatsNamed(_Named):

51 '''(INTERNAL) Base class.

52 '''

53 _n = 0

54

55 def __add__(self, other):

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

57 '''

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

59 f += other

60 return f

61

62 def __float__(self): # PYCHOK no cover

63 '''Not implemented.'''

64 return _NotImplemented(self)

65

66 def __int__(self): # PYCHOK no cover

67 '''Not implemented.'''

68 return _NotImplemented(self)

69

70 def __len__(self):

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

72 '''

73 return self._n

74

75 def __neg__(self): # PYCHOK no cover

76 '''Not implemented.'''

77 return _NotImplemented(self)

78

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

80 '''Not implemented.'''

81 return _NotImplemented(self, other)

82

83 def __str__(self):

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

85

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

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

88 '''

89 n = name or self.fcopy.__name__

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

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

92

93 copy = fcopy

94

95

96 class _FstatsBase(_FstatsNamed):

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

98 '''

99 _Ms = ()

100

101 def _copy(self, c, s):

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

103 '''

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

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

106 c._n = s._n

107 return c

108

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

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

111 '''

112 notOverloaded(self, xs, sample=sample)

113

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

115 '''Accumulate and return the current count.

116

117 @see: Method C{fadd}.

118 '''

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

120

121 def fmean(self, xs=None):

122 '''Accumulate and return the current mean.

123

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

125

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

127

128 @see: Method C{fadd}.

129 '''

130 if xs:

131 self.fadd(xs)

132 return self._M1.fsum()

133

134 def fmean_(self, *xs):

135 '''Accumulate and return the current mean.

136

137 @see: Method C{fmean}.

138 '''

139 return self.fmean(xs)

140

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

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

143

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

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

146 I{population} value (C{bool}).

147

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

149

150 @see: Method C{fadd}.

151 '''

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

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

154

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

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

157

158 @see: Method C{fstdev}.

159 '''

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

161

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

163 '''Accumulate and return the current variance.

164

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

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

167 I{population} value (C{bool}).

168

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

170

171 @see: Method C{fadd}.

172 '''

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

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

175

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

177 '''Accumulate and return the current variance.

178

179 @see: Method C{fvariance}.

180 '''

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

182

183 def _iadd_other(self, other):

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

185 '''

186 if isinstance(other, _Scalar):

187 self.fadd_(other)

188 else:

189 try:

190 if not islistuple(other):

191 raise TypeError(_SPACE_(_invalid_, _other_))

192 self.fadd(other)

193 except Exception as x:

194 raise _xError(x, _SPACE_(self, _iadd_, repr(other)))

195

196 @property_RO

197 def _M1(self):

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

199 return self._Ms[0]

200

201 @property_RO

202 def _M2(self):

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

204 return self._Ms[1]

205

206

207 class Fcook(_FstatsBase):

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

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

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

211 and Jarque-Bera normality.

212

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

214 WikiPedia.org/wiki/Algorithms_for_calculating_variance>}.

215 '''

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

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

218

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

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

221

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

223 '''

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

225 if name:

226 self.name = name

227 if xs:

228 self.fadd(xs)

229

230 def __iadd__(self, other):

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

232

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

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

235

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

237

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

239

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

241

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

243 '''

244 if isinstance(other, Fcook):

245 nb = len(other)

246 if nb > 0:

247 na = len(self)

248 if na > 0:

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

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

251

252 n = na + nb

253 n_ = float(n)

254 D = A1 - B1 # b1 - a1

255 Dn = D / n_

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

257 nab = na * nb

258 Dn3 = Dn2 * (D * nab)

259

260 na2 = na**2

261 nb2 = nb**2

262 A4 += B4

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

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

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

266

267 A3 += B3

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

269 A3 += Dn3 * (na - nb)

270

271 A2 += B2

272 A2 += Dn2 * (nab / n_)

273

274 B1n = B1 * nb # if other is self

275 A1 *= na

276 A1 += B1n

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

278

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

280 self._n = n

281 else:

282 self._copy(self, other)

283 else:

284 self._iadd_other(other)

285 return self

286

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

288 '''Accumulate and return the current count.

289

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

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

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

293 I{population} value (C{bool}).

294

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

296

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

298

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

300

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

302

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

304 Algorithms_for_calculating_variance>}.

305 '''

306 n = self._n

307 if xs:

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

309 for x in _2Floats(xs):

310 n1 = n

311 n += 1

312 D = x - M1

313 Dn = D / n

314 if Dn:

315 Dn2 = Dn**2

316 if n1 > 1:

317 T1 = D * (Dn * n1)

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

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

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

321 T1 = D * Dn

322 T2 = _0_0

323 T3 = T1 * Dn2

324 else:

325 T1 = T2 = T3 = _0_0

326 M4 += T3

327 M4 -= M3 * (Dn * _4_0)

328 M4 += M2 * (Dn2 * _6_0)

329

330 M3 += T2

331 M3 -= M2 * (Dn * _3_0)

332

333 M2 += T1

334 M1 += Dn

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

336 self._n = n

337 return _sampled(n, sample)

338

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

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

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

342

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

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

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

346

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

348

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

350 '''

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

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

353 s = self.fskewness(sample=sample)

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

355

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

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

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

359

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

361 '''

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

363

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

365 '''Accumulate and return the current kurtosis.

366

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

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

369 I{population} value (C{bool}).

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

371

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

373

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

375 and U{Mantalos<https://www.researchgate.net/publication/

376 227440210_Three_different_measures_of_sample_skewness_and_kurtosis_and_their_effects_on_the_JarqueBera_test_for_normality>}.

377

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

379 '''

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

381 if n > 0:

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

383 m2 = float(M2 * M2)

384 if m2:

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

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

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

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

389 x *= n**2 / d

390 if excess:

391 K -= x

392 k = K.fsum()

393 return k

394

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

396 '''Accumulate and return the current kurtosis.

397

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

399 '''

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

401

402 def fmedian(self, xs=None):

403 '''Accumulate and return the current median.

404

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

406

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

408

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

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

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

412 and method L{Fcook.fadd}.

413 '''

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

415 # median = mean - skewness * stdef / 3

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

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

418

419 def fmedian_(self, *xs):

420 '''Accumulate and return the current median.

421

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

423 '''

424 return self.fmedian(xs)

425

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

427 '''Accumulate and return the current skewness.

428

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

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

431 I{population} value (C{bool}).

432

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

434

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

436 and U{Mantalos<https://www.researchgate.net/publication/

437 227440210_Three_different_measures_of_sample_skewness_and_kurtosis_and_their_effects_on_the_JarqueBera_test_for_normality>}.

438

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

440 '''

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

442 if n > 0:

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

444 m2 = pow(float(M2), _1_5)

445 if m2:

446 S = M3 * (sqrt(float(n)) / m2)

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

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

449 s = S.fsum()

450 return s

451

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

453 '''Accumulate and return the current skewness.

454

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

456 '''

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

458

459 def toFwelford(self, name=NN):

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

461 '''

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

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

464 f._n = self._n

465 return f

466

467

468 class Fwelford(_FstatsBase):

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

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

471

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

473 '''

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

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

476

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

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

479

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

481 '''

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

483 if name:

484 self.name = name

485 if xs:

486 self.fadd(xs)

487

488 def __iadd__(self, other):

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

490

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

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

493

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

495

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

497

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

499

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

501 WikiPedia.org/wiki/Algorithms_for_calculating_variance>}.

502 '''

503 if isinstance(other, Fwelford):

504 nb = len(other)

505 if nb > 0:

506 na = len(self)

507 if na > 0:

508 M, S = self._Ms

509 M_, S_ = other._Ms

510

511 n = na + nb

512 n_ = float(n)

513

514 D = M_ - M

515 D *= D # D**2

516 D *= na * nb / n_

517 S += D

518 S += S_

519

520 Mn = M_ * nb # if other is self

521 M *= na

522 M += Mn

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

524

525# self._Ms = M, S

526 self._n = n

527 else:

528 self._copy(self, other)

529

530 elif isinstance(other, Fcook):

531 self += other.toFwelford()

532 else:

533 self._iadd_other(other)

534 return self

535

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

537 '''Accumulate and return the current count.

538

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

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

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

542 I{population} value (C{bool}).

543

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

545

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

547

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

549

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

551 '''

552 n = self._n

553 if xs:

554 M, S = self._Ms

555 for x in _2Floats(xs):

556 n += 1

557 D = x - M

558 M += D / n

559 D *= x - M

560 S += D

561# self._Ms = M, S

562 self._n = n

563 return _sampled(n, sample)

564

565

566 class Flinear(_FstatsNamed):

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

568 C{RunningRegression} computing the running slope, intercept

569 and correlation of a linear regression.

570 '''

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

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

573

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

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

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

577 or L{Fwelford}).

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

579

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

581 L{Fwelford}.

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

583 '''

584 _xsubclassof(Fcook, Fwelford, Fstats=Fstats)

585 if name:

586 self.name = name

587

588 self._S = Fsum(name=name)

589 self._X = Fstats(name=name)

590 self._Y = Fstats(name=name)

591 if xs and ys:

592 self.fadd(xs, ys)

593

594 def __iadd__(self, other):

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

596

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

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

599

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

601

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

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

604 are not compatible.

605

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

607

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

609 '''

610 if isinstance(other, Flinear):

611 if len(other) > 0:

612 if len(self) > 0:

613 n = other._n

614 S = other._S

615 X = other._X

616 Y = other._Y

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

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

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

620 self._n += n

621 self._S += S + D

622 self._X += X

623 self._Y += Y

624 else:

625 self._copy(self, other)

626 else:

627 try:

628 if not islistuple(other):

629 raise TypeError(_SPACE_(_invalid_, _other_))

630 elif isodd(len(other)):

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

632 self.fadd_(*other)

633 except Exception as x:

634 raise _xError(x, _SPACE_(self, _iadd_, repr(other)))

635 return self

636

637 def _copy(self, c, s):

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

639 '''

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

641 c._n = s._n

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

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

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

645 return c

646

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

648 '''Accumulate and return the current count.

649

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

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

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

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

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

655 I{population} value (C{bool}).

656

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

658

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

660

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

662

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

664 '''

665 n = self._n

666 if xs and ys:

667 S = self._S

668 X = self._X

669 Y = self._Y

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

671 n1 = n

672 n += 1

673 if n1 > 0:

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

675 X += x

676 Y += y

677 self._n = n

678 return _sampled(n, sample)

679

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

681 '''Accumulate and return the current count.

682

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

684 positional values (C{Scalar}s).

685

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

687 '''

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

689

690 def fcorrelation(self, sample=False):

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

692

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

694 I{population} value (C{bool}).

695 '''

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

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

698

699 def fintercept(self, sample=False):

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

701

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

703 I{population} value (C{bool}).

704 '''

705 return float(self.y._M1 -

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

707

708 def fslope(self, sample=False):

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

710

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

712 I{population} value (C{bool}).

713 '''

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

715

716 def _sampled(self, t, sample):

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

718 '''

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

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

721

722 @property_RO

723 def x(self):

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

725 '''

726 return self._X

727

728 @property_RO

729 def y(self):

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

731 '''

732 return self._Y

733

734

735__all__ += _ALL_DOCS(_FstatsBase, _FstatsNamed)

736

737# **) MIT License

738#

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

740#

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

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

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

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

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

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

747#

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

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

750#

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

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

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

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

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

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

757# OTHER DEALINGS IN THE SOFTWARE.