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_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 # pow from .fmath

24

25__all__ = _ALL_LAZY.fstats

26__version__ = '23.07.21'

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_op_, 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/227440210>}.

376

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

378 '''

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

380 if n > 0:

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

382 m2 = float(M2 * M2)

383 if m2:

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

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

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

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

388 x *= n**2 / d

389 if excess:

390 K -= x

391 k = K.fsum()

392 return k

393

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

395 '''Accumulate and return the current kurtosis.

396

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

398 '''

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

400

401 def fmedian(self, xs=None):

402 '''Accumulate and return the current median.

403

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

405

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

407

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

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

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

411 and method L{Fcook.fadd}.

412 '''

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

414 # median = mean - skewness * stdef / 3

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

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

417

418 def fmedian_(self, *xs):

419 '''Accumulate and return the current median.

420

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

422 '''

423 return self.fmedian(xs)

424

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

426 '''Accumulate and return the current skewness.

427

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

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

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

431

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

433

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

435 and U{Mantalos<https://www.researchgate.net/publication/227440210>}.

436

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

438 '''

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

440 if n > 0:

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

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

443 if m2:

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

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

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

447 s = S.fsum()

448 return s

449

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

451 '''Accumulate and return the current skewness.

452

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

454 '''

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

456

457 def toFwelford(self, name=NN):

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

459 '''

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

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

462 f._n = self._n

463 return f

464

465

466 class Fwelford(_FstatsBase):

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

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

469

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

471 '''

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

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

474

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

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

477

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

479 '''

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

481 if name:

482 self.name = name

483 if xs:

484 self.fadd(xs)

485

486 def __iadd__(self, other):

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

488

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

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

491

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

493

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

495

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

497

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

499 WikiPedia.org/wiki/Algorithms_for_calculating_variance>}.

500 '''

501 if isinstance(other, Fwelford):

502 nb = len(other)

503 if nb > 0:

504 na = len(self)

505 if na > 0:

506 M, S = self._Ms

507 M_, S_ = other._Ms

508

509 n = na + nb

510 n_ = float(n)

511

512 D = M_ - M

513 D *= D # D**2

514 D *= na * nb / n_

515 S += D

516 S += S_

517

518 Mn = M_ * nb # if other is self

519 M *= na

520 M += Mn

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

522

523# self._Ms = M, S

524 self._n = n

525 else:

526 self._copy(self, other)

527

528 elif isinstance(other, Fcook):

529 self += other.toFwelford()

530 else:

531 self._iadd_other(other)

532 return self

533

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

535 '''Accumulate and return the current count.

536

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

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

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

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

541

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

543

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

545

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

547

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

549 '''

550 n = self._n

551 if xs:

552 M, S = self._Ms

553 for x in _2Floats(xs):

554 n += 1

555 D = x - M

556 M += D / n

557 D *= x - M

558 S += D

559# self._Ms = M, S

560 self._n = n

561 return _sampled(n, sample)

562

563

564 class Flinear(_FstatsNamed):

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

566 C{RunningRegression} computing the running slope, intercept

567 and correlation of a linear regression.

568 '''

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

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

571

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

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

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

575 or L{Fwelford}).

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

577

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

579 L{Fwelford}.

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

581 '''

582 _xsubclassof(Fcook, Fwelford, Fstats=Fstats)

583 if name:

584 self.name = name

585

586 self._S = Fsum(name=name)

587 self._X = Fstats(name=name)

588 self._Y = Fstats(name=name)

589 if xs and ys:

590 self.fadd(xs, ys)

591

592 def __iadd__(self, other):

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

594

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

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

597

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

599

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

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

602 are not compatible.

603

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

605

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

607 '''

608 if isinstance(other, Flinear):

609 if len(other) > 0:

610 if len(self) > 0:

611 n = other._n

612 S = other._S

613 X = other._X

614 Y = other._Y

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

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

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

618 self._n += n

619 self._S += S + D

620 self._X += X

621 self._Y += Y

622 else:

623 self._copy(self, other)

624 else:

625 try:

626 if not islistuple(other):

627 raise TypeError(_SPACE_(_invalid_, _other_))

628 elif isodd(len(other)):

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

630 self.fadd_(*other)

631 except Exception as x:

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

633 return self

634

635 def _copy(self, c, s):

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

637 '''

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

639 c._n = s._n

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

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

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

643 return c

644

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

646 '''Accumulate and return the current count.

647

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

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

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

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

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

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

654

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

656

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

658

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

660

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

662 '''

663 n = self._n

664 if xs and ys:

665 S = self._S

666 X = self._X

667 Y = self._Y

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

669 n1 = n

670 n += 1

671 if n1 > 0:

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

673 X += x

674 Y += y

675 self._n = n

676 return _sampled(n, sample)

677

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

679 '''Accumulate and return the current count.

680

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

682 positional values (C{Scalar}s).

683

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

685 '''

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

687

688 def fcorrelation(self, sample=False):

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

690

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

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

693 '''

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

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

696

697 def fintercept(self, sample=False):

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

699

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

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

702 '''

703 return float(self.y._M1 -

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

705

706 def fslope(self, sample=False):

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

708

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

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

711 '''

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

713

714 def _sampled(self, t, sample):

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

716 '''

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

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

719

720 @property_RO

721 def x(self):

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

723 '''

724 return self._X

725

726 @property_RO

727 def y(self):

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

729 '''

730 return self._Y

731

732

733__all__ += _ALL_DOCS(_FstatsBase, _FstatsNamed)

734

735# **) MIT License

736#

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

738#

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

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

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

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

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

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

745#

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

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

748#

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

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

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

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

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

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

755# OTHER DEALINGS IN THE SOFTWARE.